Changelog

What's new in TraceBug

Every release, straight from the repo. New capture features, agent-workflow improvements, and the bugs we caught in the bug-catcher.

v1.11.0
2026-10-13

Launch-readiness release. Phase 2: integration tokens and the AI key move out of the websites you test and into the extension's own storage (2a), and every send to GitHub, Linear, Slack or an AI provider now happens inside the extension, behind a confirmation you see first (2b).

Security
  • ⚠️ Rotate your tokens.
    If you configured GitHub, Linear, Slack or an AI key before 1.11, revoke and re-create it, then enter the new one in TraceBug settings. Earlier versions stored these in each website's own localStorage, where any script on that site could read them.
  • Tokens and API keys are no longer stored on the pages you test
    (src/ext-transport.ts, src/integrations/tracker-client.ts, src/ai/llm-client.ts) — in extension mode they live in chrome.storage.local. The extension's SDK runs in the host page's main world, so page localStorage was readable by the site under test. The page-world code now never receives a secret: the getters return nothing in extension mode, and "is GitHub configured?" is answered from a non-secret boolean snapshot the content script pushes in, so nothing had to become async.
  • Secrets are no longer typed into the page either
    (tracebug-extension/options.html, options.js) — there is a new TraceBug settings page (options_ui, opens in a tab, Chrome + Firefox). The old in-page forms both collected tokens inside the host page's DOM *and* pre-filled them as attribute values, so a page script could read a stored token the moment the modal opened. The in-page modal now links to settings instead. Saved values are never echoed back into a field — the page shows a "Configured" pill.
  • Old page-stored keys are migrated automatically
    (tracebug-extension/secret-migration.js) — when TraceBug next runs on a site that still holds tracebug_integrations / tracebug_ai_config (or the legacy tracebug_ai_key), they are moved into extension storage and deleted from the site. If extension storage already holds a config, the existing one is kept and settings shows a one-time notice naming the site whose older token was dropped, so nothing disappears silently.
  • Provider calls no longer run in the page
    (src/providers/api.ts, tracebug-extension/background.js, provider-bridge.js) — GitHub, Linear, Slack and the AI providers are called by the extension's background worker, which reads the token from its own storage. Previously every call was a fetch() from the page's main world, so a site that had replaced window.fetch could read the Authorization header even when it could not read the stored token. The page now hands over the report content only — no repository, team, channel, webhook, base URL or key — and an unexpected field is rejected rather than ignored, at both the content script and the worker.
  • Every send to GitHub, Linear or Slack is confirmed first
    (tracebug-extension/confirm-send.html) — a window the extension owns shows which site asked, where it is going, the title and the full report body, and warns when the body contains a remote image (a read receipt for whoever hosts it) or an @mention (those notify real people). Nothing is sent until you click Send; Cancel, closing the window and a two-minute timeout all send nothing. A send cancelled this way no longer falls back to the copy/prefill path — it just stops. Approving is accepted only from that window, never from a page.
  • AI analysis is approved per site, then rate limited
    the first analysis on a site asks once and is remembered (revocable under "Sites allowed to run AI analysis" in TraceBug settings); after that each site is capped at 10 analyses a minute, so a page cannot quietly spend your API credits.
  • Slack delivery is now confirmed for real
    (src/providers/api.ts) — with access to hooks.slack.com the worker is not bound by page CORS, so it reads Slack's actual reply instead of assuming success. A rejected webhook now says why (invalid_payload, no_service) instead of showing a bare "✓ Sent to Slack".
  • Provider failures read as sentences
    401, 403, 404, 422, rate limits and network failures become plain messages ("GitHub rejected the token. Re-create it and enter the new one in TraceBug settings."), keeping the provider's own wording where it helps.
Permissions
  • Added optional_host_permissions
    for api.github.com, api.linear.app, hooks.slack.com, api.openai.com, api.anthropic.com, localhost and 127.0.0.1 — requested from TraceBug settings when you save that integration, and only the one host it talks to. Optional host permissions add no install warning and do not disable the extension for existing users.
  • Added the alarms permission
    the send confirmation times out after two minutes, and that timer has to survive the service worker being shut down mid-decision. alarms triggers no install warning.
  • Site access is now optional: the install warning is gone
    (tracebug-extension/build/manifest.base.json) — <all_urls> moved from host_permissions to optional_host_permissions, so a fresh install asks for no permissions at all instead of "Read and change all your data on all websites". Capture on the page you are looking at rides activeTab, granted by your own click. TraceBug asks for site access only for the thing that genuinely needs it — continuing to capture after you navigate to another site — from the popup, with the reason stated first. Declining is fine: capture keeps working on the current page and says so. Existing users keep the access they already granted (docs/testing/upgrade-host-permission.md is the test that proves it).
  • A custom Ollama base URL now works
    (tracebug-extension/options.js, background.js) — saving an Ollama provider pointed at any host requests access to that one address, instead of refusing anything that was not localhost or 127.0.0.1. Only a URL that is not parseable HTTP is still rejected, and it says so in the settings page rather than at send time.
  • The keyboard shortcut (Ctrl/Cmd+Shift+B) keeps capturing the page you are on without any site access — it is a page-level shortcut inside TraceBug, so it cannot ask for a permission itself. When access has been withheld it now says that the capture covers this page only, and where to allow more.
Changed
  • A new install's capture no longer asks to share your screen
    (src/index.ts, tracebug-extension/popup.js) — Capture Bug now records the console, the network, your clicks and an interactive DOM replay, with no screen-share picker and no operating-system permission. Previously the first thing a brand-new user met was Chrome's "Choose what to share" dialog, before they had seen TraceBug do anything. Screen recording is paused in this release while we finish it — it is back in 1.11.1. If you had it on, your setting is remembered and comes back with it; you will not have to choose again. The npm SDK records video as before. - The DOM replay is what lands in the exported .html in place of the video: tens or hundreds of KB of inspectable DOM events instead of tens of MB of opaque WebM. A report from an events-only capture carries everything a video report does except the pixels. - The capture now survives navigation by default, which the video path could not always do, and the on-page HUD comes back on the new page with its timer still running. One limitation worth knowing: the DOM replay covers the page the capture ends on, while the console, network and action timeline span every page you crossed. - The HUD drops Pause and the microphone (there is nothing to pause or mute) and keeps Stop, Screenshot, Draw and Blur. Blur is not a video-only tool: it masks the element's text in the DOM replay too.
  • One line before your browser asks what to share
    (src/ui/pre-record.ts) — the share dialog is the browser's own, it offers three surfaces, and it says nothing about which one a bug report wants. Picking "Entire screen" records your whole desktop and picking the wrong tab records the wrong page, neither of which can be undone afterwards. TraceBug now explains it first, and the wording matches the dialog you are about to see: "already set to this tab — choose Share" when Capture is Current tab, "pick this tab" when it's Desktop / window.
  • "Having trouble?" in the popup, with nothing phoning home
    (tracebug-extension/diagnostics.js) - Copy diagnostics puts TraceBug's version, your browser and OS, the permissions you've granted and TraceBug's own recent internal errors on your clipboard. Report a TraceBug bug opens a prefilled GitHub issue you read and submit yourself. Neither sends anything: there is no upload path in that file at all. No URLs, no page content, no stored values and no captured data go in - error messages and stack traces are scrubbed of hostnames, file paths, emails, tokens and long number runs, stack frames are reduced to TraceBug's own code, and a provider origin you added yourself is counted rather than named.
  • A big export warns you before you wait for it
    (src/ui/quick-bug.ts) - if the estimate is over 20 MB the modal says so and names GitHub's 25 MB attachment limit, with one click to Export without video (keeping the report, console, network and screenshots) or to download the recording separately.
  • A canvas page says what the replay can't show
    (src/replay-fidelity.ts) - on pages that draw their content in a <canvas> (charts, maps, WebGL), the interactive replay renders an empty space, because recording canvas pixel-by-pixel would make captures enormous. TraceBug now says so when the capture starts, while turning on video is still an option, instead of leaving you to discover it in the report. Console, network and your clicks are unaffected.
  • "Jira" is now "Copy for Jira"
    it copies the report as Jira markup for you to paste. It never filed anything, and the old label implied it did.
  • Issues you file carry one credit line, and you can turn it off
    (src/integrations/issue-signature.ts) - GitHub, Linear and Slack bodies end with "Reported with TraceBug". It is one line, it links to the listing, and Issue credit: on/off in the export menu removes it - an issue body gets written into someone else's tracker and stays there, so that has to be your call. Slack posts previously ended with an unlinked "Reported via TraceBug"; that is now the same single line, linked.
  • The TraceBug line in an exported report can be turned off
    (src/exporters/html-template.ts) - the footer of an exported .html now links straight to the extension listing rather than the marketing site, so someone who receives your report can install it in one step. And because exports get attached to public issues and sent to clients, Export footer: on/off in the export menu removes it entirely.
  • An optional, once-or-twice review ask
    (src/ui/review-ask.ts) - after your third exported report, in a later session than your first, a small card asks whether TraceBug saved you time. Dismiss it and it asks once more, much later; dismiss that and it never asks again. No ratings on display, no counts, no incentives, and nothing is gated on answering.
  • A blocked page now tells you which page
    (tracebug-extension/popup.js, page-support.js) - the "can't run here" panel shows the host and path above the explanation, with an icon that matches the reason. When TraceBug has no site access it cannot see the URL, so the card is left out rather than showing an empty row.
  • Uninstalling asks one optional question
    (website/app/bye) - removing TraceBug opens a short page with seven reasons, an optional note, and a Skip that is as easy to click as Send. The link carries the version number and nothing else, which the page says in plain text.
  • Updating tells you what changed, without taking over a tab
    (tracebug-extension/popup.js) - after an update the popup shows a short "What's new" card the next time you open it, with a "Got it" that dismisses it for good. No new tab, no page stealing focus while you were doing something else. A fresh install never sees it: it gets the welcome page instead.
  • A bundled welcome page, so first run works offline
    (tracebug-extension/welcome.html) — installing used to open tracebug.dev/try.html in a new tab, which meant the first thing you ever saw of TraceBug depended on your connection. The welcome page now ships inside the extension: pin it to the toolbar, try it on the bug playground, and your capture shortcut — showing the key your browser actually bound, or an easy way to set one if it bound nothing. It also says plainly what a capture can see, and what never leaves your machine. The playground is still one click away, as step 2.
  • The exported replay says which page it covers
    (src/exporters/html-template.ts) — the heading on a DOM-replay export now reads "Replay of the last page (/checkout/payment)" instead of just "Replay". The console, network and action lists beside it span every page the capture crossed, so a recipient — usually not the person who captured it — could reasonably read a bare "Replay" as covering all of them and conclude the earlier pages had nothing on them. A screen recording does keep rolling across navigations, so the video export is unchanged, and so is the timeline scrubber's own label.
  • Your video setting is remembered for 1.11.1
    (tracebug-extension/popup.js, background.js) — screen recording is paused in the extension this release, but the preference it would use is kept, not cleared. If you had video on, it comes back on. That required moving the setting into the extension's own storage: it lived in each popup's page storage, which the part of TraceBug that handles installs and updates cannot read, so "chose not to record video" and "has never been asked" were the same missing value.
  • "Capture: Current tab" steers the npm SDK only, and the extension now says so
    (tracebug-extension/offscreen.js, docs/chrome-extension.md) — in the extension the share dialog always shows the full tab/window/screen choice. It cannot do otherwise: the recorder runs in an offscreen document, and the browser hint for "record the current tab" means *the tab of the page that asked*, which an offscreen document does not have. An earlier 1.11 build tried to pass that hint anyway and broke recording outright. The docs no longer describe the SDK's behaviour as if it were the extension's.
Fixed
  • A browser blocked by macOS no longer looks like you changed your mind
    (src/capture-errors.ts, tracebug-extension/offscreen.js) — if your browser is not enabled under System Settings → Privacy & Security → Screen Recording, getDisplayMedia fails with the same error name as closing the share dialog, and TraceBug reported both as "cancelled". There was no way to find out that the fix was in System Settings. It now reads the error text to tell them apart, says where the setting is, mentions the part everyone misses (the browser has to be quit and reopened), and offers a Capture without video button that gets you the capture you came for. On the popup's capture path this failure used to be entirely silent — no dialog, no capture, no message at all.
  • A blocked page now says which kind of blocked, and offers a way out
    (tracebug-extension/page-support.js, popup.js) — the popup's "Can't run on this page" panel used to be a dead end that also missed three real cases: the Chrome Web Store and the other add-on galleries (the page a brand-new user most likely has open), file:// pages when "Allow access to file URLs" is off, and the built-in PDF viewer. Each one now gets its own sentence plus a single button — Try it on a demo page, Open extension settings or Reload tab.
  • The capture shortcut now works on any tab, from a cold start
    (tracebug-extension/manifest.json, background.js) — it used to be a keydown handler inside the injected TraceBug, so it did nothing until you had already started TraceBug on that tab, which is not what the quickstart said. It is now a real browser shortcut, Alt+B by default, and it captures the current tab whether or not TraceBug is running there. Ctrl+Shift+B is not the default because Chrome will not give it up — it toggles the bookmarks bar, and Alt+Shift+B focuses it, so Chrome silently leaves both unbound. Ctrl+Shift+B still opens Quick Bug once TraceBug is on the page.
  • Nothing hardcodes the shortcut any more
    (tracebug-extension/popup.js, src/ext-transport.ts) — the popup tip and the in-page tip show whatever key your browser has bound, and offer Set a shortcut when there is none. Rebind or clear it at chrome://extensions/shortcuts (Firefox: Add-ons → Manage Extension Shortcuts). If you rebind it onto Ctrl+Shift+B, one press still opens Quick Bug once, not twice.
  • An extension update no longer destroys a recording in progress
    (tracebug-extension/background.js) — runtime.onUpdateAvailable was not handled at all, so an auto-update landing mid-recording tore down the worker and the recorder together, and the video only exists in the recorder's memory until the stop finalizes it. TraceBug now holds the update until the recording has ended and been saved, then applies it. With nothing recording it applies right away.
  • A tab left open across an extension update works again
    (tracebug-extension/background.js, content-script.js) — the worker now pings the page before dispatching anything, and repairs a tab that does not answer. Previously the dead copy of TraceBug on such a tab could not be reached, the orphan errors were suppressed on purpose to keep the page's console clean, and the dispatch failure was swallowed, so every popup action on that tab did nothing at all and said nothing. The repair replaces the bridge only when the page still has a running TraceBug, so no second toolbar appears; if the tab still cannot be reached, the popup says "Reload this tab to capture" and offers the button.
  • An action that fails explains itself in English
    (tracebug-extension/page-support.js) — raw browser strings like "Cannot access contents of the page…" or "Receiving end does not exist" are mapped to a sentence with a next step attached, and the ones carrying an instruction take over the popup instead of flashing past in a 2.5-second toast. An unrecognised error is shown verbatim rather than replaced with a vague catch-all.
  • Error and warning rows in the Actions tab say what happened
    (src/action-chips.ts) — an error row showed the literal text [object Object], and a console.warn row showed only the word "Warning" with no content. Collectors emit the text nested as data.error.message; the Actions builder was reading data.message and stringifying the error object when that came up empty. It now shows the message, falls back to the error name (a TypeError with no message is still worth reading), and never stringifies an object.
v1.10.1
2026-07-29

Store-readiness release: fixes from a Chrome Web Store-style review — permission minimization, a hardened page↔extension bridge, and four real extension-lifecycle bugs the happy path had been hiding.

Fixed
  • Removed the unused tabs permission
    the extension never needed it (every chrome.tabs.* call rides activeTab or host permissions), and it added a "Read your browsing history" install warning for nothing.
  • Page→extension bridge is now allowlisted
    page scripts can only relay the six tb:rec:* recording verbs; previously a page could relay arbitrary message types (including CAPTURE_SCREENSHOT, reading the tab's pixels back without a consent prompt).
  • tb:rec:started now actually reaches the page
    it was broadcast via runtime.sendMessage, which never delivers to content scripts, so the HUD-mount fallback, the mic-missing toast, and the slow-picker recovery (2-min cap + DOM replay re-arm) were dead code end-to-end. Background now fans it out per-tab like the auto-stop broadcast.
  • Service-worker hydration race
    a mutating event that *woke* the worker (tab close, toolbar ✕, auto-stop) could persist blank state over every tab's saved state, and the in-flight hydration read could then resurrect the stale snapshot. All mutating paths now await hydration, and a dirty flag makes fresh writes win.
  • Firefox recorder-window id survives event-page restarts
    previously a restart mid-recording meant closing the popup never ended the recording on-page, passive probes reported "not recording", and the next start opened a second window.
  • The recorder host is closed after every recording on both browsers
    Chrome's offscreen document (exempt from auto-teardown) used to hold the full base64 recording in memory until browser close; recovery is served from chrome.storage.local instead.
  • minimum_chrome_version: 116
    declared — Chrome ≤108 used to install the extension and silently fail to record.
v1.10.0
2026-07-29

The Firefox release: the extension now ships for Firefox 128+ from the same source as Chrome, with honest handling of every platform difference (gesture-gated screen picker, opt-in site access, no tab audio). Also makes locally saved tickets eviction-proof with storage insights, and hardens both flows with fixes from a full multi-agent review — including a cross-browser Sentry-mode bug where 📸 captures could attach a stale video.

Added
  • Firefox extension
    (tracebug-extension/build/build-ext.mjs, background.js, offscreen.*) — the extension now builds and packages for Firefox (115+) from the same source as Chrome. Firefox has no offscreen API, so the recorder page runs in a small popup window instead; because Firefox's getDisplayMedia requires a user gesture *in that document*, the popup shows a Share screen button that opens the native picker (Chrome's invisible flow is unchanged). Closing the recorder popup mid-recording tears the on-page HUD down cleanly. npm run zip:ext now also produces the AMO upload zip (tracebug-firefox-v<version>.zip), and the Firefox manifest declares data_collection_permissions: none — TraceBug collects nothing.
  • Saved tickets are eviction-proof
    (src/storage.ts, src/ui/quick-bug.ts, src/compact-toolbar.ts) — tickets saved with Save Ticket now live until explicitly deleted: storage-pressure eviction and session rotation only ever touch unsaved sessions, saves are verified before "Saved" is shown (with a screenshot-dropping fallback that's committed only on success and marked on the ticket card), and a warning fires at ~90% storage usage — before saves start degrading, not after. The Saved Tickets list is un-capped (scrollable) and gains per-ticket sizes, a storage meter with a room-for-≈N-more estimate, one-click Export (.html replay), and a two-click delete confirm. Storage-pressure incidents are counted locally (TraceBug.getStorageStats(), never transmitted) as the decision gate for the future IndexedDB migration.
Fixed
  • Sentry-mode 📸 Capture could attach a stale or missing video
    (cross-browser) — the capture reply told the page to fetch the clip from storage, but the capture was never persisted there; each rolling capture now persists before replying (tracebug-extension/offscreen.js).
  • Firefox parity hardening
    Xray-safe content-script→page events (cloneInto), one-time host-permission request at the first capture (Firefox treats site access as opt-in), injection failures surface in the popup instead of a false success, screenshots capture the requester's window (not whichever window has focus), recordings persist via direct chrome.storage.local when available (no multi-MB IPC round-trip), the recorder popup never opens for passive probes and closes itself after stop/cancel, clipboard writes no longer throw unhandled activation errors after async exports, and a recording recovered via broadcast (slow picker) re-arms the 2-minute cap and DOM replay.
  • Saved-ticket storage engine
    a refused write (storage full of saved tickets) is now side-effect-free (no in-memory event loss), re-saving an already-saved ticket can't un-save it, deleteSession preserves un-flushed data when freeing space, storage warnings are throttled (one per incident, not one per second), and failed flushes back off instead of re-serializing every second.
v1.9.0
2026-07-22

The fix-loop release: the report stops being evidence an agent *reads* and becomes something it *iterates against* — run the generated failing test, patch, re-run until green. Also ships inspect mode (design-QA style evidence), pre-recording blur, an extension redaction UI, and a full production-audit hardening pass (bounded recording memory, capped console capture, modal focus trap, faster source-map resolution, e2e in CI).

Added
  • Generated failing Playwright test
    (src/exporters/playwright-test.ts) — every export now embeds a runnable spec that replays the captured session (locator preference: data-testid → id → aria-label → role+name → captured CSS selector) and asserts the captured failure is gone: failed requests are collected and the specific endpoint must stop failing; console errors must stop being thrown. Red while the bug exists, green after the fix. Redacted input values become TODO placeholders with a comment. Available three ways: Download failing test (.spec.ts) in the Quick Bug More menu, embedded in the .html export payload, and via the MCP get_playwright_test tool. New SDK exports: generatePlaywrightTest, playwrightTestFilename. Input/select events now also capture a stable CSS selector (clicks already did).
  • Source-map stack resolution
    (cli/source-map.ts, MCP resolve_stack) — maps the report's minified stack frames (assets/index-ab12.js:1:43210) to original source files/lines using .map files discovered in the repo the MCP server runs from. Dependency-free V3 decoder (base64 VLQ, ~150 lines) keeps the published CLI at zero runtime dependencies. searchDir targets a custom build-output folder.
  • MCP tool get_fix_context
    one-call fix starter: the failing request with response snippet, the user action that triggered it, the first error with source-map-resolved top frames, and whether a generated failing test is available. The investigation guide now routes agents to the new tools; the server exposes nine tools total.
  • Extension UI for redaction rules
    (tracebug-extension/popup.*, content-script.js, tracebug-init.js) — the popup gains a collapsible 🛡 Redaction rules section (field names + regex patterns, validated on save) so extension users get the 1.8.0 redact config without writing code. Rules sync via chrome.storage.sync and reach the page-world SDK through <html data-tb-redact> (the same bridge as the CSP-proof player URL); a MutationObserver pushes edits into an already-running SDK live via setRedactRules.
  • Blur is element-level now — click to blur/unblur
    (src/ui/blur-tool.ts) — rebuilt from drag-rectangles to Traeco-style element picking: hover highlights, click applies filter: blur(12px) to the element itself, click again unblurs, Undo on the arming bar removes the last. Because the blur is part of the element's own rendering it moves in the same paint as the content — it physically cannot lag behind scrolling (the old viewport-fixed boxes exposed the text during fast scrolls, and even frame-tracked overlays trail by a frame). Blurred elements also get tb-mask, so the rrweb DOM replay masks their text, not just the video pixels. Blurs persist through the recording and are restored (original inline filters preserved) when it stops. HUD blur icon changed from an ambiguous cloud to the standard droplet.
  • Pre-recording options — blur first, then roll
    (src/ui/pre-record.ts, TraceBug.prepareRecording(), extension popup) — the popup gains a ⚙ Record options panel (persisted): capture surface (current tab / desktop picker), start delay (3s/5s with an on-page 3-2-1 countdown overlay), and Blur before recording — activates the existing blur tool *before* capture starts, with a floating "● Start recording / Cancel" bar, so sensitive areas are redacted with real backdrop blur from the very first frame (cancel clears the boxes). Options flow popup → background → content script → prepareRecording({ blurFirst, delaySec, surfaceMode, withMicrophone }), which is also public SDK API. The in-recording HUD already had pause/mic/screenshot/draw/blur — this closes the pre-roll gap.
  • Inspect mode — style evidence for design-QA bugs
    (src/inspect-mode.ts, src/style-evidence.ts) — "the button looks wrong" now ships with the receipts. A DevTools-style inspect mode (extension popup → Inspect element, or TraceBug.activateInspectMode()): hover paints the box-model highlight (margin/padding/content tint) plus a tooltip with the computed-style summary; click attaches the element to the report as an inspect annotation carrying a curated ~20-property style snapshot — typography, colors as hex, box model, layout — plus a WCAG text-contrast verdict (ratio + AA pass/fail, large-text aware). Existing annotate-mode annotations capture the same evidence. Surfaced everywhere: expandable "Styles" block on modal annotation cards, an Element Evidence section in generated GitHub issues, the export's Description panel, and structured elementAnnotations in the payload so MCP agents get it from get_bug_report (the investigation guide points visual bugs at it — diff captured styles against the design tokens in the repo). Deliberately *not* a site-explorer (palette/assets/responsive is VisBug's job) — this exists so visual bugs are fixable from evidence. New exports: captureStyleEvidence, formatStyleSummary, contrastRatio, cssColorToHex, activateInspectMode/deactivateInspectMode/isInspectModeActive.
v1.8.0
2026-07-21

The launch-feedback release — every feature here traces to a Product Hunt comment. Visible, configurable redaction (a masked-values summary in the export flow, app-specific redact rules for PII the token patterns can't know), console.info capture with warn/info rendered properly in the repro timeline, a .zip export because GitHub issues accept .zip but reject .html, and issue actions inside the exported report so the file's recipient can file the ticket, not just read the evidence.

Added
  • Redaction summary — the pipeline is finally visible
    (src/redaction-summary.ts, src/ui/quick-bug.ts, src/exporters/html-replay.ts) — the export modal footer and the exported report's Info tab now show exactly what the sanitizers masked, e.g. 🛡 3 sensitive values auto-masked (1 token, 2 URL params), counted by category (tokens, URL params, form fields, storage values) by scanning the built report for the [REDACTED] markers. Omitted at zero — pattern matching can't promise a clean bill of health. New SDK exports: summarizeRedactions, formatRedactionSummary.
  • Configurable redaction rules
    (src/sanitize/custom-redaction.ts, redact in TraceBugConfig) — declare app-specific PII the built-in token shapes can't know about: redact.fields (name-based, case-insensitive substring — covers form/input names, storage keys, URL query params, and JSON/urlencoded keys inside console output and response snippets) and redact.patterns (custom regexes masked anywhere in captured text). Applied at capture time on top of the built-in masking; counted in the auto-masked summary; invalid patterns are skipped so a typo can't break capture. New exports: setRedactRules, RedactRules.
  • console.info capture + warn/info in the repro timeline
    (src/collectors.ts, src/timeline-builder.ts) — info was previously not captured at any level; it now rides the "warnings" tier (error + warn + info). Warn/info/log events render as their message in the timeline (⚠ / ℹ prefixes) instead of a raw JSON dump. Each non-error level is capped at the first 50 calls per session, and TraceBug's own [TraceBug] diagnostics are never captured.
  • Download .zip (attach to GitHub)
    (src/exporters/zip-export.ts, More menu) — the same offline replay wrapped in a .zip, because GitHub issues accept .zip attachments by drag-and-drop but reject bare .html. Zero-dependency ZIP writer (~150 lines): CompressionStream("deflate-raw") when available, STORE fallback, CRC-32, UTF-8 names. Generated GitHub issues now end with a Full Repro Replay section telling the reporter to drag the .zip in. New exports: exportSessionAsZip, buildZipBlob.
  • Issue actions in the exported report
    (src/exporters/html-template.ts, src/exporters/html-replay.ts) — the file's recipient is usually the person who files the ticket, but the viewer was read-only. The header now has Open GitHub issue (prefilled github.com/…/issues/new URL, shown when the exporter configured githubRepo) and Copy issue markdown (fully offline, pastes into any tracker). Both are precomputed at export time — zero issue-builder code ships in the viewer, and the markdown derives from the already-redacted report. Token-based integrations deliberately stay out of the shareable file. HtmlReplayOptions.githubRepo added.
  • Official MCP Registry metadata
    (packages/tracebug/server.json, mcpName in the CLI package) — the tracebug npm package now carries the registry's verification marker and a server.json for mcp-publisher publish under io.github.prashantsinghmangat/tracebug.
  • Marketing screenshot pipeline
    (e2e/marketing-screenshots.mjs) — Playwright script that drives the live sandbox (record → trigger bugs → Quick Bug modal → region screenshot → export) and screenshots the exported viewer, producing current-build product shots on demand.
Changed
  • Severity is labeled as machine-classified
    the modal Info tab, exported Info rows, and the viewer's header badge tooltip now say "Severity (auto)"; the modal's Priority dropdown placeholder shows the severity-derived suggestion (Priority (auto: Medium)) with a tooltip clarifying it isn't exported unless explicitly picked. Auto severity kept being mistaken for a tester's triage call.
  • Docs match the shipped captureConsole default
    (docs/api-reference.md, docs/configuration.md, docs/bug-reporting.md) — the default has been "all" since 1.7.0 but the docs still claimed "errors"; the tier table now includes console.info and the per-level caps, and the privacy section gained a full "what gets redacted" table with honest limitations.
Fixed
  • Unbounded rrweb buffer during long recordings (OOM risk)
    (src/rrweb-recorder.ts) — the DOM-replay event array had no cap, so a multi-hour "record all day" session could accumulate hundreds of MB and crash the tab before export. rrweb now emits a fresh full snapshot every ~2 minutes (checkoutEveryNms), and the buffer is trimmed back to the most recent full snapshot past a soft cap (with a hard ceiling as a floor guarantee) — the retained tail always stays replayable. wasDomBufferTrimmed() reports when a session was windowed. Pure trimEventBuffer() is unit-tested.
  • Console/error messages could bloat the report (and drop on circular refs)
    (src/collectors.ts) — captured console arguments and error messages/stacks are now capped at 10 000 chars (…[truncated]), so a single console.error("x", hugeObject) can't retain a multi-MB string in the event buffer and localStorage. The new formatConsoleArgs is also circular-safe: a logged circular object degrades to [unserializable] instead of throwing and silently dropping the whole event.
  • Quick Bug modal now traps focus
    (src/ui/quick-bug.ts) — Tab / Shift+Tab cycle within the dialog instead of escaping to the inert page behind it (WCAG 2.4.3 / 2.1.2). Escape-to-close and focus-restore-on-close were already present; this completes the modal's keyboard semantics. trapModalTab / getFocusableElements are unit-tested.
  • CI now runs the browser e2e suite
    (.github/workflows/ci.yml) — an e2e job builds the real SDK bundle and runs the headless Playwright checks (blur tracking, pre-record flow, inspect mode) so regressions in the most complex recorded flows are caught automatically. The e2e scripts are now path-portable (were hardcoded to a local absolute path).
  • Unified sensitive-query-param detection
    (src/url-hygiene.ts isSensitiveParamName) — the capture-time URL redactor and the upload sanitizer kept two separate key lists that had drifted (one caught sig/signature, the other session/sid/csrf/pwd; each under-redacted what the other caught). Both now share one matcher that is a strict superset of both — neither path ever redacts less than before.
  • Faster source-map resolution
    (cli/source-map.ts) — a map's mappings are now decoded once and cached (per map object), and the map-file path cache is module-scoped across calls. Previously every stack frame re-decoded every mapping line up to its target and re-walked the repo for the bundle — seconds on a large map with a deep repo; now one decode + an O(segments-in-line) lookup. clearSourceMapCache() added for long-lived servers.
  • Light-mode muted text meets WCAG AA
    (src/theme.ts) — --tb-text-muted was #82828C (3.8:1 on white, fails AA for normal text); now #75757E (4.56:1).
  • Long standard recordings warn past 1 GB
    (tracebug-extension/offscreen.js) — a multi-hour non-rolling recording accumulates all chunks in the offscreen document; it now logs a one-time warning past 1 GB advising rolling mode (the default), rather than silently growing. No hard cap — truncating the video could lose the bug.
  • Unified "is this our UI element?" check for the mode tools
    (src/dom-helpers.ts) — annotate, blur, and inspect each carried a near-identical ancestor walk with drifted prefix sets; they now share one isTraceBugUiElement. Deliberately matches only widget prefixes (tracebug-/bt- ids, tracebug-/tb-qb/tb-hud/tb-rs classes, data-tracebug) and not bare tb-, because tb-mask/tb-block are redaction markers applied to host-page content — a broad match would treat a blurred element as our own UI and break second-click-to-unblur (caught by e2e, locked in by a test). The capture-path check in collectors is intentionally left separate — broadening it could drop host events.
  • init() split into named phases
    (src/index.ts) — the ~250-line init() was extracted into six private phase methods (_validateConfig, _applyThemeFromConfig, _wireQuickBugConfig, _buildEmit, _startCollectors, _mountDashboardIfEnabled, _restoreVideoSessionOnInit) with the orchestrator now ~80 lines. Pure internal refactor — no public-API or behavior change (verified: 303 unit tests + all e2e green). The TraceBugSDK facade split flagged in the audit stays deferred to a major version.
  • Tokens logged to the console reached the offline export unmasked
    (src/collectors.ts) — the token-shape scrub ran at capture only for network response snippets; console messages and error stacks were scrubbed only on the cloud-upload path, which the offline .html export never runs. sanitizeTokenShapes now applies at capture to console error/warn/info/log messages and to window.onerror / unhandled-rejection messages and stacks — a logged Bearer/JWT can no longer enter the report object at all.
  • console_warn/console_log rendered as truncated JSON in the timeline
    (src/timeline-builder.ts) — they fell into the default JSON.stringify case instead of showing the message.
v1.7.0
2026-07-17

The launch-polish release. A full Slate Indigo rebrand across every surface (widget, exports, extension, website — with "Trace", the pixel-sprite mascot), the exported replay slims down ~3–4× via gzip, issue-tracker exports file real GitHub/Linear/Slack items with your own token, and the website gains a live sandbox running the actual SDK plus a 15-second real-capture demo video. Zero lint warnings, dead code removed, adoption friction audited and fixed.

Added
  • Slate Indigo brand + "Trace" the pixel mascot
    (src/theme.ts, website/, tracebug-extension/) — one palette across all five token surfaces (widget, replay export, AI export, extension popup, website): slate-tinted near-black neutrals, a single refined indigo accent (#6366F1), green reserved strictly for success. New 8-bit mascot appears in the website flow diagram, 404, footer, hero headline (as the blinking caret), and the extension's "can't run here" state — crisp at every size because it *is* pixels. All icons (favicon, extension, apple-touch) regenerated.
  • Live sandbox — the real widget on the website
    (website/public/try.html) — a checkout page with two intentional bugs (a TypeError and a failing POST) running the actual IIFE SDK bundle. Visitors trigger a bug, capture it with the real toolbar, and export a genuine self-contained report. Linked from the demo section; the extension opens it as the first-install welcome tab.
  • Real demo video
    (website/public/tracebug-demo.webm) — a 15-second captioned screen recording of TraceBug catching a live crash on the sandbox (recorded by scripting the real product with Playwright, so re-cuts are a script run). Embedded in the website demo section with lazy mount, reduced-motion handling, and a poster fallback; replaces the scripted walkthrough iframe and the stale README GIF.
  • MCP tool picker on the hand-off card
    (src/ui/quick-bug.ts) — the post-export card now shows the one-time setup command for Claude Code / Cursor / VS Code with per-tool copy, instead of assuming Claude Code.
  • First-install welcome
    (tracebug-extension/background.js) — installing the extension opens the live sandbox with a tailored "try your first capture" banner instead of nothing.
  • npm run zip:ext
    reproducible Chrome Web Store upload zip, versioned from package.json into releases/.
  • Website: flow diagram, changelog page, and motion polish
    a Userback-style "everything connects" section with measured SVG beams; a /changelog page rendered from this file at build time; a self-assembling hero report card (events stream in, skeletons hand off to the root-cause box); cursor-spotlight cards; OS-level dark-mode auto-detect; a DevTools console easter egg.
  • html2canvas fallback for script-tag consumers
    (src/screenshot.ts) — when the lazy import("html2canvas") can't resolve (no bundler — e.g. the IIFE bundle on a plain page), the SDK now picks up a page-provided window.html2canvas UMD global, so screenshots work outside bundled apps and the extension.
  • Real issue-tracker integrations (BYO-token)
    (src/integrations/tracker-client.ts) — the GitHub / Linear / Slack export buttons now create a real issue/message via the provider's API using the user's own token, instead of only prefilling a URL or copying markdown. The call goes directly from the browser to the provider — token in localStorage, no OAuth, no TraceBug backend — the same privacy pattern as the BYO-key AI Debugger. GitHub uses a PAT (POST /repos/{owner}/{repo}/issues), which also lifts the ~6–8 KB URL-prefill truncation cap (the full report body travels in the request body); Linear uses a personal API key (GraphQL issueCreate, returns the issue URL + identifier); Slack uses an incoming webhook (fire-and-forget). A 🔗 Configure integrations modal (More menu) stores per-provider tokens; when a provider is configured its export button files for real, otherwise it falls back to the existing URL/copy flow. Jira Cloud is intentionally excluded (CORS-blocked for browser XHR) and stays copy-markup. New SDK exports: createTrackerIssue, createGitHubIssue, createLinearIssue, sendSlackMessage, getIntegrationsConfig/setIntegrationsConfig/clearIntegrationsConfig, hasIntegration. (src/ui/quick-bug.ts, tests/tracker-client.test.ts — 11 tests)
  • Self-contained DOM replay export (rrweb)
    (src/rrweb-recorder.ts, src/exporters/html-replay.ts, src/exporters/html-template.ts) — Export .html now embeds an interactive DOM replay instead of the multi-MB base64 video. rrweb records a full DOM snapshot plus incremental mutation events (KB, not MB); the exported .html inlines rrweb's Replayer runtime and reconstructs the page in a sandboxed <iframe>, scaled-to-fit, with a play/seek control bar and click-to-seek from the Console/Network/Events rows. Inputs are masked and .tb-block/.tb-mask respected; inlineImages embeds images so the replay is genuinely offline (no requests back to the original site). The Replayer runtime is regenerated by npm run gen:rrweb (a prebuild hook, gitignored). Falls back to the screenshot gallery if the Replayer or DecompressionStream is unavailable.
  • Compressed replay stream
    (gzipToBase64 in html-replay.ts; inflateRrweb in html-template.ts) — the rrweb event stream is gzip-compressed via the browser-native CompressionStream and base64-embedded (rrwebEventsGz), then inflated at load with DecompressionStream. Repetitive DOM JSON compresses ~8–12×, cutting a typical replay file ~3–4× (measured 2.96 MB → 0.87 MB) with a byte-exact round-trip. No bundled compression library. The Export .html button's size estimate reflects the compressed size.
  • Export for AI (.html)
    (More menu; generateAiHtml/exportReportAsAiHtml in src/exporters/ai-prompt.ts) — a tiny (~5 KB) text-only HTML report for users who want to paste/upload a report into a chat (Claude/ChatGPT) without MCP. Renders the same capped/deduped structured content as the .md export — no rrweb runtime, no DOM blob, no base64 media — so it fits well under a chat's context limit. HTML-escaped throughout.
Changed
  • npx tracebug init is now honest and actually useful
    (cli/bin.ts) — it no longer claims to "set up" the project or print "Done! ready" when it only echoes a snippet. It now states plainly that it prints (doesn't install/edit), and — the real value — leads with the framework gotcha developers actually trip on: the Next.js App-Router "use client" + useEffect requirement, Nuxt's .client.ts SSR rule, and Svelte's onMount. All snippets now include the dev-only enabled: "auto" flag with an explainer. Website "CLI setup" tab updated to match.
  • shadcn (new-york) UI re-skin
    the injected widget (src/theme.ts), the exported replay viewer (src/exporters/html-template.ts), the AI export (src/exporters/ai-prompt.ts), and the extension popup (tracebug-extension/styles.css) all now share the website's brand palette (now the Slate Indigo palette — indigo-500 primary, slate near-black dark, hairline borders, 10/12/16 radii). Buttons match new-york variants (glow primary, active press, focus-visible rings via --tb-ring); the Quick Bug modal is a rounded-2xl card. Hardcoded off-palette colors in the draw/annotate tools were tokenized.
  • prebuild hook regenerates the rrweb runtime
    src/exporters/rrweb-runtime.generated.ts is gitignored and rebuilt by a prebuild step wired into all build* scripts, so a fresh clone builds cleanly.
  • Zero lint warnings
    cleared all 177 (no-explicit-any ×116, no-unused-vars ×61) with real types, not suppressions: proper axe-core/DOM/Web-Speech typings, small interfaces for provider API responses, unknown + narrowing for genuinely dynamic values. No eslint-disable, no as any. DrawRegion now officially includes the pen shape it always stored.
  • Dead code removed
    the cut v1 onboarding tour machinery (the surviving logo pulse now actually injects its keyframe — it silently never animated before), six dead toolbar functions and write-only state, orphaned website components, stale release zip, and the pre-rebrand docs/demo.gif.
  • Adoption audit fixes
    npx tracebug help pointed at a dead domain; the README's Chrome Web Store badge was a broken URL; docs led with --dir while the homepage taught the zero-config MCP form; the popup now surfaces the Ctrl+Shift+B shortcut and explains the mic toggle; example-app/ gained a README; the website sandbox SDK copy is auto-synced by every build.
Fixed
  • The exported replay no longer shows TraceBug's own UI
    (src/rrweb-recorder.ts) — rrweb was recording the injected widget (#tracebug-root, the compact toolbar) and the blank draw canvas into the DOM stream, so they rendered on top of the user's page in the exported .html replay (an empty box + the floating toolbar). A blockSelector now excludes TraceBug's own overlay UI from the capture while keeping the draw SVG overlay, so the replay shows only the user's page + the annotations.
  • Pen drawing is smooth, not laggy
    (src/draw-mode.ts) — freehand strokes re-stroked the *entire* growing path on every pointer move (O(n²), so it got laggier the longer you drew) and were raw polylines. Now each move draws only the newest segment (O(1)) and all strokes are quadratic-smoothed (canvas, the committed render, and the SVG replay path).
  • Pen/draw annotations now appear in the exported replay
    (src/draw-mode.ts) — during a recording, freehand pen strokes and shapes are drawn on a <canvas>, which rrweb's DOM replay can't capture (recordCanvas is off), so they showed in the modal's video preview but vanished from the exported .html DOM replay. Each committed shape is now mirrored as an SVG element in the page DOM with the same appear→fade lifecycle, so rrweb records it as ordinary DOM mutations and the annotations replay at the exact moment they were drawn — no file-size cost, no recordCanvas.
  • Event capture no longer stops on full-page navigation
    (src/index.ts, src/storage.ts, src/compact-toolbar.ts) — the Track session (events-only) capture now survives clicking a link to another page. A persisted capture mode (tracebug_active_capture_mode) lets the SDK resume an event-only session on the next page (keeping one session id across the whole flow) while still finalizing a *video* session whose tab-share Chrome ended. Also: durable flush on pagehide/visibilitychange:hidden (bfcache-safe), no duplicate session:start per navigation, and the toolbar's tracking state is restored after the load.
  • MCP server finds reports with zero setup
    (cli/mcp-server.ts, cli/bin.ts) — get_bug_report (and the other get_* tools) now resolve a report by bare filename or fragment from the user's Downloads/Desktop folders — where browser exports land — in addition to --dir. Previously the copy-pasted hand-off prompt failed unless the server was pointed at the download folder. Shared folders are shallow-scanned and name-prefiltered (tracebug-*) to stay fast. list_bug_reports widens to those folders only when the server started without an explicit --dir (an explicit --dir stays scoped to that folder). The generated hand-off prompt (generateMcpPrompt) drops the --dir requirement. (src/exporters/ai-prompt.ts, tests/mcp-server.test.ts)
v1.6.0
2026-07-09

The agent-workflow release. Three ways to get a bug in front of an AI — and none of them phone home: failed Playwright tests become the same agent-ready .html reports (upload as a CI artifact), BYO-key LLM analysis runs browser-direct against Anthropic/OpenAI/Ollama with your key never leaving the page, and HAR export hands you your network capture as a standard file you own. Built on the v1.5 local MCP foundation.

Added
  • HAR export
    (src/exporters/har-export.ts) — a new 🌐 Export HAR button in the Quick Bug modal writes the captured network activity as a standard HAR 1.2 (HTTP Archive) file that opens in Chrome/Firefox DevTools, Charles, Fiddler, and Postman. Reshapes the request/response data TraceBug already captures (method, url, status, timing, query string parsed into name/value pairs, failed-response bodies with a guessed mime type) into the spec — no new capture, no dependency. Spec-optional fields we don't capture (headers, cookies) are emitted as empty arrays / -1 sentinels per the HAR schema, so the output validates. New SDK exports buildHar (pure) and exportSessionAsHar (build + download). Jam markets "everything a HAR offers" but ships no HAR export — this owns that axis. Docs: docs/har-export.md. (src/ui/quick-bug.ts, tests/har-export.test.ts — 8 tests)
  • AI Debugger — BYO-key LLM analysis
    (src/ai/llm-client.ts) — the AI tab can now run real LLM root-cause analysis using the user's own API key (Anthropic / OpenAI / local Ollama). The call goes directly from the browser to the provider — no TraceBug backend — and the key is stored only in localStorage. The prompt (the same structured one the "copy prompt" flow builds) is scrubbed of secret token shapes via the existing sanitizer before it leaves the page; screenshots/video are never sent. The model returns markdown (Root cause / Evidence / Where to look / Suggested fix / Edge cases), rendered inline. Replaces the "coming soon" stub with a real provider+key+model config modal (migrates the legacy tracebug_ai_key bare-key storage). New SDK exports: runLLMAnalysis, buildAnalysisPrompt, getAIConfig/setAIConfig/clearAIConfig/hasAIKey, DEFAULT_MODELS, ANTHROPIC_MODEL_CHOICES, PROVIDER_LABELS. Completes the "private AI debugging" story (local heuristic + local MCP + BYO-key LLM). Docs: docs/ai-debugger.md. (src/ui/quick-bug.ts, tests/llm-client.test.ts — 11 tests)
  • Playwright reporter
    (tracebug-sdk/playwright) — every failed test writes a self-contained TraceBug .html report to outputDir (default bug-reports/): assertion error + Playwright code snippet, test.step()/pw:api timeline as repro steps and timeline events, failure screenshots from attachments, project metadata, and a local root-cause hint (5xx → high, 4xx/page error → medium, assertion-only → low). Optional traceBugPage fixture adds page console, uncaught errors, network requests with failed-response bodies, and navigations (attached only on failure, 500-entry caps). Reports are written only for the final retry; the reporter can never fail the run; output is verified readable by the MCP server (fuzzy title lookup + investigation guide). Zero runtime deps — the module imports nothing from @playwright/test (structural types), so it builds and tests without Playwright installed. New ./playwright subpath export, Node-side tsup entry, and tsconfig.node.json for typechecking Node code (cli/, src/reporters/). Docs: docs/playwright.md. (src/reporters/playwright.ts, tests/playwright-reporter.test.ts — 5 tests)
v1.5.0
2026-07-08

The MCP release: TraceBug exports become agent-ready. npx -y tracebug mcp gives Claude Code / Cursor / VS Code read access to every exported bug report — fully local, zero network connections.

Added
  • Standalone tracebug npm package
    new ~24 KB, zero-dependency CLI package (packages/tracebug) publishing just bin.mjs (MCP server + init), so a developer who only *receives* bug reports runs npx -y tracebug mcp without pulling the full SDK. tracebug-sdk still bundles the same CLI; npm run build:cli generates the package's bin from dist/bin.mjs. (packages/tracebug/, cli/bin.ts version lookup now supports both layouts)
  • MCP server
    npx -y tracebug mcp [--dir <path>] starts a local Model Context Protocol server so AI coding agents (Claude Code, Cursor, Windsurf, VS Code) can read exported bug reports and debug from them. Six tools: list_bug_reports, get_bug_report, get_console_errors, get_network_activity, get_repro_steps, get_screenshot (returns real image content). Reads the self-contained .html exports directly (the embedded tb-data payload) — fully local, stdio-only, zero network connections, zero new dependencies (the JSON-RPC loop is hand-rolled). Includes a demo report (demo-bug-reports/sample-report.html) and a pre-configured .mcp.json so a fresh clone works with Claude Code out of the box. Docs: docs/mcp.md. (cli/mcp-server.ts, cli/bin.ts, tests/mcp-server.test.ts)
  • Investigation guide
    get_bug_report now returns a prioritized "what to fetch next" list computed from what the report actually contains (e.g. [HIGH] get_network_activity — 2 failed requests captured…), so agents spend their tool calls on the data that matters for that specific bug. Network leads when the root-cause hint blames a request; console leads otherwise; screenshots are promoted to a primary source when no errors were captured. (cli/mcp-server.ts)
  • Forgiving report resolution
    every MCP tool's file argument now accepts a path, a bare filename found anywhere under the scan dir, or a case-insensitive fragment of the filename or report title; unresolvable names error with the list of available reports. (cli/mcp-server.ts)
  • debug_bug_report MCP prompt
    the server now advertises the prompts capability with one prompt (optional file argument): the standard hand-off ("load the report, follow its investigation guide, cross-reference the codebase, propose a fix"). In Claude Code: /tracebug:debug_bug_report. (cli/mcp-server.ts)
  • Agent hand-off prompt in the extension
    after every Export .html, the Quick Bug modal shows the paste-into-Claude-Code/Cursor prompt for that export (auto-copied to the clipboard) with a link to the MCP setup guide. New generateMcpPrompt() exported from the SDK. (src/ui/quick-bug.ts, src/exporters/ai-prompt.ts)
  • Agent hand-off prompt in the export itself
    the exported .html viewer's AI tab now carries a "Debug with a coding agent (MCP)" card with the same prompt and a copy button, so whoever receives the file learns how to feed it to their agent. The filename is recovered from the file:// URL at view time, so the prompt survives renames. The shipped demo report (demo-bug-reports/sample-report.html) was regenerated as a full viewer export, so it demos both the interactive replay and the agent card. (src/exporters/html-template.ts)
  • Website: /docs/mcp page
    docs/mcp.md is now on the website as a full docs page (workflow, hand-off prompt, Claude Code/Cursor/VS Code setup, tools reference, privacy, troubleshooting), linked from the docs TOC, a promo card on /docs, the footer Resources column, and the sitemap. (website/app/docs/mcp/page.tsx)
v1.4.0
2026-07-06

The hardening pass that made Phase 1 (fully offline) production-ready.

Production hardening & bug-fix pass (July 2026)
  • Removed committed Supabase credentials
    website/.env.local untracked from git; .gitignore now covers .env.local / .env.*.local. Keys must be rotated (they remain in git history).
  • cloudEndpoint validation
    new src/cloud-endpoint.ts with resolveCloudEndpoint(): HTTPS enforced (plain HTTP only for localhost), javascript:/data:/malformed URLs rejected with fallback to the production endpoint. Applied in the auth bridge constructor, all SDK call sites, share-link exporter, and the Quick Bug modal. Unit-tested.
  • Chrome extension manifest: explicit content_security_policy for extension pages.
  • Stale video bundled into unrelated exports / fresh video missing from the modal.
    The global last-recording had no session binding and the ownership check existed only in the modal. New shared getSessionVideo() gate in report-builder.ts (recording must start after the session was created, 10 s transport grace) used by both buildReport() and the modal; the toolbar record flow now arms the session *before* capture starts; historical tickets strip the video explicitly. (src/report-builder.ts, src/compact-toolbar.ts, src/ui/quick-bug.ts)
  • Modal video played only a few seconds of long recordings.
    Chrome MediaRecorder WebM lacks a duration header; the player now forces Chrome to compute the real duration (seek-past-end workaround) when the reported duration is Infinity/short. (src/ui/quick-bug.ts)
  • iframe-bridge.ready() could hang forever and leak a message listener
    when the bridge iframe never loaded — added a 20 s timeout that unmounts, cleans up, and allows retry. (src/auth/iframe-bridge.ts)
  • deleteSession() silently dropped up to 1 s of pending events
    from surviving sessions — pending flush now runs before the read. (src/storage.ts)
  • Uncaught (in promise) Error: No tab with id
    spam from the extension service worker — badge API promises are now awaited inside the try/catch. (tracebug-extension/background.js)
  • Clipboard copy failures now show "✗ Copy failed" instead of failing silently (src/dashboard.ts); "Fix with AI" button no longer renders white-on-white on hover in light theme; six ESLint errors fixed (emoji regex u flags, stray expression, extra semicolon); pushScreenshot() side-path now respects the 50-screenshot memory cap.
  • Exports now carry the user's edited title and description
    ; the description renders below the Replay (matching the ticket modal) instead of hiding in a tab. Notes and Description tabs removed from the export viewer (tabs: Info / Console / Network / Actions / AI / Events).
  • Tester-assigned priority is surfaced everywhere
    🚩 priority chip in the export header next to the auto-severity badge; priority flows user → session → report → all exports.
  • Cookies captured
    (non-HttpOnly only) alongside localStorage/sessionStorage with the same redaction rules; shown in the modal Info tab and export, scrubbed again by the cloud sanitizer.
  • Info tab cleanup: TraceBug's own tracebug_* storage keys filtered at capture, empty env rows dropped, per-row storage icons removed, explicit "Web storage — empty on this page" state.
  • Ticket modal: description editor open by default; Notes tab renders only when the session has annotations.
  • CI now runs ESLint and the test suite on every push/PR; package.json declares engines: node >=18.
Cloud sharing portal (Phase 6)
  • TraceBug.shareReport()
    uploads the bug report to TraceBug's cloud and returns a public URL like https://tracebug.dev/share/<id>. Recipient opens it in a browser without installing anything. (src/exporters/share-link.ts)
  • Sign-in via Supabase magic link
    TraceBug.signIn() / TraceBug.signOut() / TraceBug.getCurrentUser(). Auth happens in a hidden iframe pointed at tracebug.dev/sdk-bridge, so the customer's site never sees the auth token. (src/auth/iframe-bridge.ts, website/app/sdk-bridge/page.tsx)
  • 🔗 Share link button
    in the Bug Ticket modal next to "📦 Export .html". Shows inline spinner during upload; opens the public viewer in a new tab on success. (src/ui/quick-bug.ts)
  • Per-user quotas
    5 active video shares + 10 active screenshot shares, max 5 screenshots per share, 50 MB upload cap. Enforced both client- and server-side. (website/lib/quotas.ts)
  • Screenshot trim picker
    when a report has more than 5 screenshots, a modal lets the user choose which 5 to upload (numbered selection, max-cap, grid view). Cancel preserves local report unchanged. (src/ui/screenshot-trim-modal.ts)
  • 2-min video duration cap for cloud uploads
    with warnings at 1:30 and 1:55, auto-stop at 2:00. Local recording stays uncapped if shareReport() is never called. (src/video-recorder.ts)
  • Pre-upload sanitization
    strips Authorization headers, password fields, and common token shapes (JWT, OpenAI sk-, Stripe, GitHub PAT, AWS keys, Slack tokens, Google API keys) from network logs, console output, and URLs before anything leaves the browser. Local download path keeps the unsanitized original. (src/sanitize/cloud-upload.ts)
  • Dashboard at /dashboard
    Jam.dev-style sidebar + grid layout with quota bars, real thumbnail previews (320×180 JPEG generated from the first screenshot or video frame at upload time), Copy / Extend +14d / Delete actions, video / screenshot filters, search. (website/app/dashboard/*)
  • Public viewer at /share/[token]
    no login required. Server fetches via service-role client with 5-min signed download URLs; renders report in sandboxed <iframe srcdoc>. OG tags so links unfurl in Slack/Teams. (website/app/share/[token]/page.tsx)
  • 14-day default retention
    , user-extendable +14d per click. Nightly pg_cron soft-deletes expired rows; hourly Netlify cron purges Storage objects. (website/supabase/migrations/0001_initial.sql, website/app/api/cron/purge-expired/route.ts)
  • Chrome extension popup gets a cloud account block
    shows signed-in email + quota (🎥 X/5 · 📸 Y/10), Sign-in button when out, dashboard link, sign-out. Reads endpoint from chrome.storage.local.tracebug_cloud_endpoint (default: production). (tracebug-extension/popup.{html,js})
  • CORS middleware
    echoes Access-Control-Allow-Origin for chrome-extension:// and moz-extension:// origins on /api/*. (website/middleware.ts)
  • docs/SHARE-PORTAL-PLAN.md
    the full design spec for the cloud sharing portal (architecture, quotas, RLS, files-changed, deployment, security checklist).
  • docs/PROJECT-CONTEXT.md
    single-doc orientation for agents/contributors picking up the project cold.
  • New table public.sessions with RLS policies (sessions_select_own / _insert_own / _update_own / _delete_own).
  • New Storage RLS on storage.objects for the reports bucket — users can only PUT/GET/DELETE under their own <user_id>/ folder.
  • New pg_cron job tracebug-expire-shares runs daily at 03:00 UTC.
  • Refactored src/exporters/html-replay.ts to extract buildReplayPayload() so the local download and cloud upload paths share the same assembly logic — touching one updates both.
  • Fixed pre-existing bug in src/video-recorder.ts where in-page recordings were silently dropped: isUsableRecording() required a dataUrl that the in-page path never generated. Now FileReader-encodes the blob → base64 dataUrl before stashing.
  • Fixed pre-existing bug where browser-native "Stop sharing" didn't trigger the toolbar UI reset (only the SDK's own Stop button did). The in-page track.ended handler now fires _onAutoStop callback too.
  • Fixed pre-existing build break in src/exporters/html-template.ts where unescaped backticks inside a comment inside a template literal confused esbuild.
Phase 5 — Console tab → unified event feed
    Phase 5 — Console tab → unified event feed
    • Console tab is now a unified, chronological event feed
      (matches Jam.dev's mental model). One scrollable story per session: console logs of every level, page navigations, network errors, user clicks/inputs/selects/submits, and video start/stop markers — all merged and sorted by timestamp.
    • Six category-filter pills
      above the list — All / Console / Page navigations / Network errors / User activity / Video — with live counts. Pills hide categories that have zero entries. Existing search input + "Errors only" toggle still apply.
    • Per-row layout
      48 px elapsed-time gutter (m:ss) | 24 px SVG category icon | message text + optional collapsed stack trace. Replaces the old card-style log entries.
    • Category-aware coloring
      navigation rows tinted info-blue, network errors tinted error-red, console errors red text, console warnings amber, video markers in accent.
    • Mirrored in the HTML export
      ([src/exporters/html-template.ts](src/exporters/html-template.ts)) — viewers reviewing an exported .html see the same unified feed with the same six pills and same filter behavior.
    Phase 4 — Video recording stabilization
    • Empty video field in exports.
      Multi-MB recording dataUrls were silently truncated by chrome.runtime.sendMessage (it drops responses >10 MB or so). The exported HTML had everything except the video bytes. Now the offscreen writes the recording into chrome.storage.local via the background service worker (which always has storage access), the content-script reads it back, and re-attaches the dataUrl to the page-side response. IPC carries metadata only. (tracebug-extension/offscreen.js, background.js, content-script.js)
    • dataUrlToBlob rejected valid base64.
      Splitting on the first , was wrong because mime types can contain commas (e.g. video/webm;codecs=vp9,opus). Switched to splitting on the literal ;base64, marker. The atob failure was the actual root cause of every previous empty-video export and the NotSupportedError on play. (src/video-recorder.ts)
    • tb:rec:auto-stopped broadcast dead-coded.
      The generic tb:rec:* handler in background.js was registered before the dedicated auto-stop handler, so the latter never ran. Reordered so tb:rec:auto-stopped matches first. (tracebug-extension/background.js)
    • content-script.js re-injection crashed
      with SyntaxError: Identifier 'REC_DATA_KEY' has already been declared. Wrapped the whole script in a window.__TRACEBUG_CS_LOADED__ guard so re-injection is a no-op. (tracebug-extension/content-script.js)
    • Double share-picker.
      Two concurrent startVideoRecording calls both passed the isActive() check (recorder doesn't exist yet — getDisplayMedia is in flight) and showed the picker twice. Added _startInFlight coalescing on both SDK and offscreen sides. (src/video-recorder.ts, tracebug-extension/offscreen.js)
    • Recording lost on page reload.
      When the recording tab navigated and Chrome ended the share, the SDK's reinit cleared the stale sessionId flag but didn't recover the finalized recording. Added a recovery path that pulls _lastBuiltRecording from the offscreen on init. (src/index.ts)
    • Auto-stop finalize race.
      The video track's ended event and the MediaRecorder's stop event raced; the early bail on isActive() could miss the broadcast. Removed the bail and added _autoStopBroadcast dedup so finalize fires exactly once regardless of which signal arrives first. (tracebug-extension/offscreen.js)
    • In-modal play threw NotSupportedError.
      v.play() returns a Promise that rejects on no-supported-sources, and the surrounding sync try/catch couldn't catch it. Added a .catch() that falls back to event-only timeline playback. (src/ui/replay-scrubber.ts)
    • Switched from silent chrome.tabCapture to getDisplayMedia picker.
      Tab capture's silent-record path failed on many sites (cross-origin, sandboxed iframes, chrome:// URLs) and produced recordings the modal couldn't play back. The picker is more reliable, lets the user record any tab / window / screen, and the surfaceSwitching: "include" option keeps the recording alive through tab navigation. (tracebug-extension/offscreen.js)
    • Recording HUD now has a "Draw" button
      that toggles the existing draw-mode (rect / ellipse / redact, 5 colors) while recording continues. HUD slides down to top: 64px so it doesn't collide with the draw toolbar. (src/ui/recording-hud.ts)
    • hydrateRecording is non-throwing.
      Even if atob fails for any reason, the recording is still returned with the dataUrl preserved, so the export still embeds the video. (src/video-recorder.ts)
    • isUsableRecording gate.
      revokeAndStash no longer overwrites a real recording with an empty stub. The auto-stop broadcast and manual stop both go through this check before storing. (src/video-recorder.ts)
    • Manifest:
      added unlimitedStorage permission for large recordings. (tracebug-extension/manifest.json)
    Phase 3 — UI overhaul + parity work
    • Minimal popup
      with one hero CTA ("Capture Bug Now") plus secondary "Record session" / "View tickets" buttons. The old toggle + 6 quick-action grid + active-sites list are gone. Three actions, three clicks max from "I see a bug" to "report filed".
    • Lazy SDK injection
      the SDK no longer loads until the user clicks a popup action. No persistent allowlist, no auto-inject on page load. Tracking is fully opt-in per session. (tracebug-extension/background.js, popup.js)
    • Silent tab capture (later replaced — see Phase 4)
      initial implementation used chrome.tabCapture.getMediaStreamId to avoid the share picker. Phase 4 reverted this to getDisplayMedia for reliability.
    • Quick Bug modal tabbed layout
      two-pane (replay left, tab strip right) with Info | Console | Network | Actions | AI | Notes tabs. Each renders real data from the BugReport. Theme toggle (🌗) in header. (src/ui/quick-bug.ts)
    • Action chips
      Actions tab renders chips with verb + HTML element preview (<button class="play-btn" type="button"> +1 more) and theme-aware syntax coloring. New module src/action-chips.ts, new BugReport.actionChips field. Text exports (GitHub/Jira/markdown) still use plain sessionSteps[].
    • Full network capture
      Network tab shows ALL requests, not just failures. New NetworkRequestEntry type + BugReport.networkRequests field. Color-coded 2xx/3xx/4xx/5xx/err status badges. (src/types.ts, src/report-builder.ts)
    • Linear integration
      src/linear-issue.ts opens https://linear.app/new?title=...&description=... with prefilled markdown.
    • Slack export
      src/slack-export.ts returns Slack-flavored text (*bold*, code blocks, > quotes) for clipboard paste.
    • Redact (blur) tool
      third shape type in draw mode, solid hatched block, no comment prompt. (src/draw-mode.ts, src/types.ts)
    • HTML export tabbed layout + dual-theme
      standalone replay file mirrors the modal layout with Info | Console | Network | Actions | AI | Notes | Events | Description tabs. Both palettes embedded as CSS variables; auto-switches via prefers-color-scheme, manual override via in-header 🌗 toggle saved per file in localStorage. (src/exporters/html-template.ts, src/exporters/html-replay.ts)
    • Theme toggle in modal header
      cycles ☀ light → 🌙 dark → 🌗 auto. Per-origin preference saved in localStorage.tracebug_theme_pref, picked up on next SDK init. (src/ui/quick-bug.ts, src/index.ts)
    • Smarter dashboard search
      renderFilteredSessions() now also matches click text, input values, aria-labels, repro steps, request URLs — not just sessionId + errorMessage. (src/dashboard.ts)
    • Theme palette refresh
      softer zinc + violet ramps replacing navy + cyan. New syntax-highlighting tokens (--tb-code-tag, --tb-code-attr-name, --tb-code-attr-val, --tb-code-text, --tb-code-bg) so HTML element previews adapt to light/dark. Default mode changed from "dark" to "auto". (src/theme.ts, src/index.ts:254)
    • Modal CSS polish
      more generous padding, real hover backgrounds (button lifts instead of dimming opacity), pill badges (border-radius 999px), refined focus rings, custom theme-aware scrollbar, larger radii, -0.01em letter-spacing on titles. (src/ui/quick-bug.ts — _injectStyles())
    • Popup enable flow no longer reloads
      the tab. SDK injects in-place via chrome.scripting.executeScript. User keeps current page state. (tracebug-extension/popup.js, background.js — INJECT_SDK_NOW handler)
    • Badge now reflects "SDK loaded on tab"
      rather than "site enabled". Clears automatically on tab reload (since injectedTabs is wiped). (tracebug-extension/background.js)
    • "Jam" references removed
      from all source comments. 17 mentions replaced with neutral wording.
    • Persistent enabled-sites allowlist (getEnabledSites / saveEnabledSites / isSiteEnabled / toggleSite and the TOGGLE_SITE / CHECK_SITE message handlers).
    • Auto-injection on page navigation.
    • Popup's 6 quick-action buttons (Annotate / Draw / Screenshot / PDF Report / GitHub Issue / Jira Ticket) — they duplicated the on-page toolbar.
    • Popup's Enable / Disable toggle row — replaced with informational hint.
    • console.error wrapper (in src/collectors.ts:collectErrors()) adds a frame at the top of every console.error stack trace once the SDK is loaded. Trace shows tracebug-sdk.js as the topmost frame; the actual caller is the line below. Fix candidates: install the wrapper only during active recording, add //# sourceURL= magic comment, or default captureConsole to "none".
    Added — Sentry Mode (Rolling Video Buffer)
    • New module: src/video-recorder.ts
      wraps getDisplayMedia + MediaRecorder. Supports two modes: - mode: "rolling" (default) — recording continues across captures; captureRollingBuffer() snapshots the in-progress recording into a finished VideoRecording while the screen-share keeps running. - mode: "standard" — classic record-then-stop flow.
    • New module: src/ui/recording-hud.ts
      floating pill with pulsing red dot, elapsed timer, "captures taken" counter, comment input (timestamped to recording time, Enter to save), 📸 Capture button (rolling mode only), and ⏹ Stop. Defensive CSS injected with !important to defeat host-page resets (Tailwind preflight, etc.).
    • Auto-capture on error
      when an unhandled error fires *and* a rolling session is armed, the existing error toast offers "Capture with video" instead of "Capture bug." One click captures the buffer + opens the ticket modal.
    • Smart Stop
      if the user already filed tickets via Capture, Stop ends silently with a toast. If no captures were taken, Stop opens the modal with the full recording (preserves the simple one-shot flow).
    • Public API:
      TraceBug.startVideoRecording({ mode?, withMicrophone?, onStatus? }) - TraceBug.stopVideoRecording(): Promise<VideoRecording | null> - TraceBug.captureRollingBuffer(): Promise<VideoRecording | null> - TraceBug.isVideoRecording() / isRollingMode() / getCaptureCount() - TraceBug.getLastVideoRecording() - downloadVideoRecording(rec, filename?) (named export)
    • Toolbar:
      new red Record button (between Region Screenshot and the right edge). Only enabled when the browser supports getDisplayMedia.
    • Exports updated:
      GitHub issue, Jira ticket, and PDF report now include a "Screen Recording" section listing the auto-downloaded .webm filename, duration, file size, and any timestamped comments. Every export action auto-downloads the .webm alongside screenshots.
    • Comments reset on capture
      each ticket gets its own set of timestamped comments. Comments accumulate during the recording; capturing snapshots them into the recording and clears the buffer for the next bug.
    Added — Auto-Scanner (Phase 2)
    • New runtime dep: axe-core@4.11.4
      ~1.4 MB, lazy-loaded via import("axe-core") so the base bundle stays light. Only loaded the first time scanPage() is called. Extension IIFE bundle grew from 770 KB → 2.17 MB.
    • New module: src/scanner/index.ts
      orchestrator. Runs all detectors in parallel via Promise.all + per-detector catch wrapper (one failure doesn't block the others). Concurrent scan() calls are coalesced. Issues live in memory only — each scan is a fresh run, results clear on reload.
    • New module: src/scanner/helpers.ts
      shared selector builder (id → data-testid → tag+nth-of-type chain), severity coercion, ID generator.
    • Detectors
      (one file each in src/scanner/detectors/): - a11y.ts — axe-core, restricted to WCAG 2.0/2.1 A+AA rules (skips noisy best-practice). Multi-element violations roll into one issue with a (+ N more) suffix. - broken-images.ts — <img> where naturalWidth === 0 && complete === true. - mixed-content.ts — http:// resources on HTTPS pages. Covers img, script, iframe, link[rel=stylesheet|preload|prefetch|manifest|icon], audio, video, source, embed, object. - session-data.ts — three detectors that classify already-collected session data: console-error (deduped by message), failed-request (4xx/5xx/network-error with response snippets), slow-api (successful requests over 2s).
    • New module: src/ui/issues-panel.ts
      modal grouped by severity. Defensive CSS with !important rules. Locate scrolls into view + outlines the element with a 2.4s purple flash. File Ticket pre-fills the Quick Bug modal via the new prefilledTitle / prefilledDescription options on showQuickBugCapture().
    • New types in src/types.ts:
      Issue, IssueDetector ("axe-a11y" | "broken-image" | "mixed-content" | "console-error" | "slow-api" | "failed-request"), IssueSeverity ("critical" | "serious" | "moderate" | "minor").
    • Public API:
      TraceBug.scanPage(): Promise<ScanResult> - TraceBug.showIssuesPanel({ rescan? }) — runs a fresh scan first by default - TraceBug.getIssues({ includeDismissed? }) - TraceBug.dismissIssue(id) / undismissIssue(id) / clearIssues() - TraceBug.getIssue(id) / getIssueCounts() (severity-bucketed counts) - Named exports: scan, getIssues, dismissIssue, etc.
    Changed — UX Cleanup (v1.0 polish pass)
    • Toolbar reduced from 10 → 6 elements:
      Logo · ⚡ Quick Bug · 🔍 Scan · 📷 Screenshot · ⬚ Region · 🔴 Record. Removed: standalone recording-state dot, Annotate button, Draw button, Annotation List button + badge, Settings card button, Help button.
    • Quick Bug modal exports reduced from 5 → 3:
      Open in GitHub · Copy as GitHub · Copy as Jira. Removed: Copy as Plain Text (GitHub markdown is pasteable anywhere), Download Screenshots (every export already auto-downloads them).
    • First-run onboarding tour removed.
      Most users skipped it. Logo pulse retained as a subtle "we're here" hint; button tooltips are the only discovery aid.
    • Source files retained for all cut features.
      src/element-annotate.ts, src/draw-mode.ts, src/onboarding.ts, src/pdf-generator.ts still ship in the bundle and remain accessible programmatically — TraceBug.activateAnnotateMode(), TraceBug.activateDrawMode(), TraceBug.downloadPdf(), replayOnboarding(). Only the default UI surface changed.
    • shortcuts.annotate and shortcuts.draw config keys retained
      in the TraceBugConfig type for backwards compatibility. They're now no-ops since the corresponding toolbar buttons aren't mounted.
    • Build artifacts:
      Extension IIFE 2.17 MB (axe-core dominates). npm DTS 32 KB. All 74 tests pass.
    Added — Freemium Plan
    • New module: src/plan.ts
      getPlan(), isPremium(), setPlan(), hydratePlan(), FREE_LIMITS. Plan exposed on the SDK singleton and as named exports.
    • New module: src/ui/upgrade-modal.ts
      minimal centered modal with a placeholder "Upgrade — Coming Soon" CTA and a dev-only toggle for flipping the plan flag locally.
    • Gates:
      Screenshots — free users capped at 2 per ticket (FREE_LIMITS.screenshots). Both takeScreenshot() and takeRegionScreenshot() return null and show the upgrade modal at the cap. Toolbar buttons enforce the same. - PDF export — TraceBug.downloadPdf() opens upgrade modal; no PDF is generated. - Jira ticket — TraceBug.getJiraTicket() returns null and opens upgrade modal. The Quick Bug modal renders the Jira button as 🔒 Jira Ticket (Premium) (muted) for free users; premium gets the blue 🎫 Copy as Jira Ticket. - Advanced metadata — consoleErrors and networkErrors arrays in the generated report are blanked out for free users. Capture still happens (cheap); only the export is redacted. - Custom branding — new companyName config option, ignored on free. On premium, every export is prefixed with > _Reported via TraceBug — {companyName}_.
    • UI surfaces:
      Toolbar settings card now shows a Free Plan / ✨ Premium badge next to the screenshot count (clickable; opens upgrade modal). Screenshot count line shows N / 2 cap on free. - Quick Bug modal footer shows the same badge alongside "Draft auto-saved".
    • Dev toggle
      the upgrade modal exposes a small Dev: enable Premium button so testers can flip the flag without redeploying. Persists across sessions.
    • Backwards compat:
      getJiraTicket() and downloadPdf() previously always succeeded for any caller; they now return null / open the modal for free users. Existing premium-equivalent flows (programmatic generateGitHubIssue, etc.) are unchanged.
    Changed — Ticket-First Capture Flow
    • Toolbar Screenshot button
      captures a full-viewport screenshot and stores it in the ticket. Toast: "Added to ticket · N screenshots". No file is written to disk.
    • Toolbar Region Screenshot button
      same flow: drag-to-crop, store in the ticket, no download.
    • TraceBug.stopRecording()
      pauses recording and auto-opens the ticket-review modal so the user sees every step + every screenshot before exporting.
    • Ticket-review modal
      (the renamed Quick Bug modal — title is now "Bug Ticket — Review & Export"): - Renders all screenshots in the active ticket as a numbered thumbnail strip below the primary preview. - Click any thumbnail to swap it into the primary preview. - Header shows screenshot count (e.g. "3 screenshots attached · download/copy includes all screenshots").
    • Bulk export
      every export action (Open in GitHub, Copy as GitHub Issue, Copy as Jira Ticket, Copy as Plain Text, Download Screenshots) downloads every screenshot in the ticket, staggered 120ms apart so the browser doesn't drop concurrent downloads.
    • Toast labels updated to reflect the count, e.g. "✓ Copied as Jira Ticket · downloading 3 screenshots".
    Added — Region Screenshot (Snipping Tool)
    • TraceBug.takeRegionScreenshot()
      drag-to-select-area screenshot. Shows a fullscreen overlay with a translucent dim layer; user drags a rectangle; the cropped PNG is pushed to getScreenshots(). Press Esc to cancel. - Reuses the existing captureScreenshot() pipeline, so it inherits chrome.tabs.captureVisibleTab in extension context and the html2canvas fallback in plain-SDK context — no new dependencies. - Crops via canvas with DPR-aware scaling (naturalWidth / window.innerWidth). - Returns null if the user presses Esc or selects a region smaller than 5×5 px. - New module: src/region-screenshot.ts - New export: captureRegionScreenshot
    • Toolbar button
      a corner-square icon next to the existing camera, tooltip "Region Screenshot — drag to select, added to ticket".
    • Overlay carries data-tracebug="region-overlay" so the existing isTraceBugElement filter ignores its own clicks.
    Added — Recording Aliases
    • TraceBug.startRecording()
      alias for resumeRecording(). Semantic only; behavior is unchanged.
    • TraceBug.stopRecording()
      calls pauseRecording() and auto-opens the ticket-review modal. The modal flow above is the only behavioral difference from pauseRecording().
    v1.3.0
    2026-04-13
    Added — Debugging Assistant
    • 🔍 Root Cause Hint Engine
      every report now leads with a one-line, confidence-tiered cause hint. Deterministic, no AI APIs. - HIGH — when a failed network request is present: "API POST /orders failed with 500 after clicking 'Place Order'" - MEDIUM — when only a runtime error is present: "TypeError suggests undefined/null data — the response or upstream value was likely missing" - LOW — click without downstream signal: "Click on 'Submit' did not trigger any observable effect" - Injected at the top of GitHub issues, Jira tickets, PDF reports, and the Quick Bug modal - New exports: generateRootCauseHint(report), formatRootCauseLine(rc) - New type: RootCauseHint { hint, confidence }
    • 🧠 Smart Bug Summary
      one-sentence TL;DR derived from network + error + click + page signals. Rendered as a blockquote on GitHub, a {panel} on Jira, and an indigo banner on PDF. - Example: "API POST /orders failed with 500 when clicking 'Place Order' button on /checkout" - New export: generateSmartSummary(report)
    • 🧩 Session Steps
      last ~10 user actions converted to plain-English strings ("Clicked 'Edit' button", "Navigated to /checkout"). FIFO queue. Injected as a numbered list in all exports. - New export: generateSessionSteps(events)
    • 🎯 Clicked Element snapshot
      structured { tag, text, selector, id, ariaLabel, testId, page } for the last click before the bug. Surfaced as a User clicked: line in exports. - New export: extractClickedElement(events) - New type: ClickedElementSummary
    Added — Network Failure Capture
    • Response body snippets
      first 200 chars of every failed fetch or XHR response, captured asynchronously after the response returns (never blocks the request). - Rendered as a collapsible <details> block in GitHub, h4. Response Snippets on Jira, monospace blocks in PDF - Fetch path clones the response and reads text on a microtask tick — the caller gets the response unchanged - XHR path reads responseText on loadend only for status >= 400 || status === 0
    • Last-10 failures ring buffer
      in-memory FIFO, ~2KB steady state, cleared on destroy() and on "Clear All Data" - New export: getNetworkFailures() and TraceBug.getNetworkFailures() - New types: NetworkFailure, NetworkErrorEntry (with optional response field)
    Fixed — Session & Data Integrity
    • Clear All Sessions was silently undone by the pending flush
      after clicking "Clear All Data", the next event re-wrote the stale in-memory cache back to localStorage. Now clearAllSessions() cancels any pending flush and drops the cache before wiping storage.
    • Deleted sessions could resurrect themselves
      deleteSession(id) now invalidates the cache; pending flushes can't re-save the deleted row.
    • Race between flush queue and mutator writes
      updateSessionError, addAnnotation, saveEnvironment previously read from localStorage, ignoring newer events in the pending cache; their writes then overwrote the cache on flush. All three now read through the cache and call scheduleFlush().
    • Network failure buffer leaked across cleared sessions
      buildReport() now filters buffer entries by timestamp >= session.createdAt so a fresh session cannot inherit failures from a cleared one.
    Changed
    • "Clear All Data" now actually clears everything
      sessions + screenshots + voice transcripts + element annotations + annotation badges + network failure buffer. Applied to both the dashboard panel's Clear button and the compact toolbar's settings card. Confirm dialog updated to list what's being wiped.
    • Every report starts with root cause + TL;DR
      GitHub issues, Jira tickets, PDF reports, and Quick Bug modal descriptions all lead with these two lines so a reader understands the bug without scrolling.
    v1.2.0
    2026-04-09
    Added — API
    • TraceBug.quickCapture()
      one-shot bug capture flow: screenshot + auto-filled modal + 1-click copy to GitHub/Jira/Text. Keyboard shortcut: Ctrl+Shift+B. Cuts bug reporting from ~7 clicks to 2.
    • TraceBug.setUser({ id, email, name })
      identify users for session attribution, persisted in localStorage
    • TraceBug.getUser() / TraceBug.clearUser()
      query and clear identified user
    • TraceBug.markAsBug()
      flag current session as a bug (adds isBug: true)
    • TraceBug.getCompactReport()
      2-sentence Slack-friendly summary of the session
    • TraceBug.getErrorCount() / TraceBug.exportSessionJSON()
      CI/CD integration helpers
    • TraceBug.use(plugin) / TraceBug.removePlugin(name)
      plugin registration API
    • TraceBug.on(event, callback)
      subscribe to lifecycle hooks (session:start, error:captured, screenshot:taken, report:generated, etc.)
    • TraceBugUser type
      exported
    • takeScreenshot({ includeAnnotations })
      option — capture page with annotation badges visible
    Added — Features
    • Theme system
      dark/light/auto themes with 45+ CSS custom property design tokens, follows prefers-color-scheme in auto mode
    • Configurable toolbar position
      toolbarPosition: 'right' | 'left' | 'bottom-right' | 'bottom-left'
    • Draggable toolbar
      drag to any position, persisted in tracebug_toolbar_pos localStorage key
    • Mobile FAB mode
      viewport < 768px collapses toolbar to a single floating action button; panel becomes full-width bottom sheet
    • First-run onboarding
      4-step tooltip tour shown once, "?" help button on toolbar to replay
    • Console capture levels
      captureConsole: 'errors' | 'warnings' | 'all' | 'none' (new event types: console_warn, console_log)
    • Tab-based session detail
      Overview / Timeline / Errors / Export tabs with sticky header (bug title + severity badge)
    • Session search & filter
      search by error message, URL, or session ID; filter by all/errors/healthy
    • Session auto-naming
      sessions named by primary page (e.g., "Login Session")
    • Toast notifications
      visual feedback for actions with aria-live announcements for screen readers
    • Clickable annotation badges
      numbered badges on annotated elements open a popover with intent, severity, and comment
    • Screenshots in annotation list panel
      alongside element annotations and draw regions, with inline previews and per-item download
    • Screenshot auto-download
      screenshots and "Save Annotated" auto-download PNG files to the user's system
    • Extension screenshot via chrome.tabs.captureVisibleTab
      replaces html2canvas in the Chrome Extension for CORS-safe captures
    • CLI tool
      npx tracebug init auto-detects framework (React, Next.js, Vue, Angular, Svelte, Nuxt, vanilla) and prints setup snippet
    • Custom keyboard shortcuts
      shortcuts: { screenshot, annotate, draw } config option
    Added — Website & SEO
    • Chrome Web Store publication
      one-click install at chromewebstore.google.com/detail/fdemmibikigigkfjngclmdheeajhdgaj
    • Cross-browser support docs
      Chrome, Edge, Brave, Opera (Chrome Web Store); npm SDK for Firefox/Safari
    • 4 programmatic SEO pages
      /compare/sentry-alternative, /compare/logrocket-alternative, /compare/bugsnag-alternative, /compare/frontend-bug-reporting-tool
    • Sitemap.xml + robots.txt
      via Next.js metadata API
    • JSON-LD structured data
      (SoftwareApplication schema) in root layout
    • Per-page metadata
      unique title/description/OG for docs page
    • OG image
      PNG via Next.js ImageResponse route (replaces SVG that social platforms couldn't render)
    • Security headers
      X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy in next.config.js
    • Scroll-reveal animations
      IntersectionObserver-based with prefers-reduced-motion respect
    • Mobile-responsive comparison table
      card layout on < md viewports
    Added — Infrastructure
    • Config validation
      runtime checks on init() (projectId required, maxEvents/maxSessions must be positive); invalid values warn and fall back to defaults
    • OSS infrastructure
      CONTRIBUTING.md, CHANGELOG.md, .github/ISSUE_TEMPLATE/ (bug_report.md, feature_request.md), .github/workflows/ci.yml
    • README badges
      npm version, monthly downloads, GitHub stars, MIT license
    • ui/ module directory
      extracted dashboard helpers (helpers.ts, toast.ts, index.ts) from the 2000-line dashboard.ts
    Fixed
    • Global error boundary
      entire init() body + all collectors + dashboard mount wrapped in try/catch; SDK never crashes host app
    • Resilient fetch/XHR wrappers
      original function ALWAYS called even if tracking throws; handles Request objects, URL objects, strings
    • Footer privacy link
      changed from # to /privacy
    • Docs page metadata
      unique title/description instead of falling back to root layout
    Changed
    • All UI components migrated from hardcoded colors to CSS custom properties (var(--tb-*))
    • Collectors hardened with try/catch — never break host app event handling
    • html2canvas pinned to exact version 1.4.1 (removed caret range)
    v1.1.1
    2026-03-12
    Initial Public Release
    • Session recording (clicks, inputs, navigation, API calls, errors)
    • Screenshot capture with annotation editor (rectangles, arrows, text)
    • Voice bug descriptions via Web Speech API
    • Element annotation mode (click elements to attach feedback)
    • Draw mode (rectangles/ellipses on live page)
    • GitHub Issue and Jira Ticket export (one-click copy)
    • PDF report generation
    • Auto-generated reproduction steps
    • Auto bug title and flow summary
    • Environment detection (browser, OS, viewport, device, connection)
    • Privacy: passwords, credit cards, SSNs, tokens auto-redacted
    • Framework noise filtering (Next.js, Webpack, Vite, Turbopack)
    • Chrome Extension (Manifest V3, CSP-safe injection)
    • npm package with ESM + CJS + IIFE + TypeScript declarations