| 2026-09-26 |

Replay at recorded pace; extension version check + hot overlay from the server
...
Replay: pauses are no longer compressed — steps fire on absolute recorded
clocks (1:1 with the recording), the cursor stays on the track, a 200ms
settle follows scrolls so the next click resolves on the scrolled page,
and the clock re-anchors after page loads (load time is not recorded time).
Version + hot overlay: the extension version now comes from the root
package.json; the server publishes the current version and the overlay
module (overlay.js + overlay.css) at /api/ext/manifest + /api/ext/assets
(POST /api/ext/publish, manifest written last as the commit point). The
content script imports the server overlay straight from its URL — public
assets answer ACAO * because page-origin imports are the only module path
that works in an isolated world (blob/data imports and eval are all
blocked by the MV3 CSP) — and falls back to the statically bundled copy
on any failure; the version gate never downgrades the UI below the core.
Core updates surface as a popup banner linking to /download; the download
page shows the published version and versioned zips (npm run package also
emits them). make ext-publish ships the overlay from a built dist.
E2E covers the whole hot-overlay round trip: publish a marked 9.9.9
overlay, reload → data-overlay-source="remote", popup banner, dead-server
fallback → "bundled", then republish the pristine assets.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
18 hours ago
|
| 2026-09-25 |
Recording: capture page scroll as a "scroll" step with replay support
...
- content script debounces window scroll (250ms per pause, like inputs)
into a "scroll" step; inner-container scrolls are ignored (they bubble
from the page, so only document/documentElement targets count)
- replay sends replay_scroll to the content script, scaling recorded
offsets to the current viewport
- "scroll" step type in shared API + server validation; timeline icon,
AI-prompt coords line and stepTypes i18n (en/ru/uk)
- e2e: tall test page + scroll assertion; wait for composer focus
instead of fixed delays (full-page stitch can take a while); mkdir
/tmp/ltt-shots for video download; debug log on failure
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
| 2026-09-18 |
Project card: buttons grouped on the right edge, title on the left
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days ago
|
Ukrainian localization; link-preview meta tags on share pages
...
- uk.json: full Ukrainian translation, uk added to SUPPORTED_LOCALES with
the shared slavic plural rules and a language option in settings
- /p/<token> and /r/<token> are now served by the API: it injects
og:title/description/url/image/video + twitter:card into the panel's
index.html so Telegram and Jira previews show the project or report
(title, description, creation date, screenshot/video). Caddy proxies
those paths to the server; the public origin comes from PUBLIC_BASE_URL
or the request host. Verified end-to-end through the prod stack.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days ago
|
Panel polish: settings spacing, drawer footer corners, login logo, register width
...
- Settings cards: 16px top margin (was bottom-only, the first card hugged
the page header)
- Drawer footer: stretch .shell-footer to full width — as a shrink-to-fit
flex item the margin-right:auto on the nickname never engaged, so the
name and the sign-out button sat together on the left
- Login card: the real BugTrail mark instead of the generic ph-bug icon
- Register card: 460px wide on desktop (kit .card max-width lifted)
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days ago
|
Project deletion in the panel; project page works without a session
...
- DELETE /api/projects/{id} existed but had no UI: danger icon button on
each project card + GnConfirmDialog (cascade handled by the DB FK).
- /p/<token> errored for logged-out visitors: the report list hit the
owner-only /projects/{id}/reports. Added a public
/projects/by-token/{token}/reports endpoint (same shape) and pointed
ProjectPage at it.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days ago
|
Extension zips: 404 instead of SPA fallback; clear errors in packaging
...
A missing zip used to fall through try_files to index.html and download
as a 'broken archive'. /ext/* is now real files only, and package.mjs
fails early with a hint when zip or the built dist is missing.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days ago
|
Download page: breathing room around download buttons and section titles
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days ago
|
Keep extension zips out of the repo — make prod builds them
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days 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
8 days ago
|
Report cards: breathing room under the section title
...
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 days 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
8 days 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 days 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 days 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 days 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 days 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
8 days 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
8 days 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
8 days 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
8 days 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
8 days 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
8 days 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
8 days 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
9 days 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
9 days 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
9 days 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
9 days 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
9 days 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
9 days 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
9 days ago
|