* feat(web): replace flow table resource icons with lucide SVGs
The resource type icons were 32x32 PNGs, so they could not be recolored and stayed dark against the dark theme's background.
The SVG icons inherit currentColor and follow the active theme.
getIcon becomes getResourceType and returns a semantic resource type rather than a CSS class name, which keeps the icon choice in the view layer and out of the column's sort key.
Removing the last raster icons also drops the Chromium BSD notice that only covered the four DevTools PNGs.
* refactor(web): size flow rows from the rowHeight prop
Flow rows were 32px tall only as a side effect of the 32x32 icon bitmap, which the virtual scroller's hardcoded rowHeight silently depended on.
Pinning the height in CSS would have restated that number in a second place and would still break for any caller overriding the prop, so rows now take their height from rowHeight directly.
This also stops rows collapsing when the icon column is hidden via web_columns.
* docs(web): drop redundant comments on rowHeight and getResourceType
* docs: tighten the changelog entry for the SVG resource icons
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* feat(web): add status/method badges and flow-table tooltips
Replace the inline-colored status text with a colored status-code badge and render the request method as an outlined badge.
Both reuse the semantic state tokens, so they follow the dark theme automatically.
Add hover tooltips (via a new optional Icon `title` prop and a title on the TLS cell) to the TLS, replay, intercept, and error indicators.
Part of #7789.
* feat(web): tone down solid state fills in the dark theme
Add a soft variant of the state palette (`--mitmweb-<state>-soft-bg`/`-fg`) and use it for the status badges and the footer option labels.
A saturated fill glares against a dark surface once it is repeated across every row, so the soft fill sinks towards the background and the label text carries the hue.
The solid tokens stay vivid for the header connection label and `.btn-info`, which appear one at a time and are meant to draw the eye.
Light values alias the existing solid colors, so the light theme is unchanged.
* feat(web): drop the now-unused status-code color tokens
The status column renders a badge that reuses the semantic state tokens, so nothing consumes --mitmweb-status-1xx…5xx/-other any more.
* [autofix.ci] apply automated fixes
* refactor(web): unify the status and method badges into one component
The two components differed only in styling, so a single presentational Badge in common/ now carries the shared class and callers pass the variant class.
statusClass moves to flow/utils next to statusCode, mirroring getIcon which likewise returns a CSS class.
* fix(web): give the status and method badges the same height
The two badges sized themselves from their own font size, padding and border, so the outlined method badge came out about a pixel shorter than the filled status badge.
A fixed height on the shared base plus a transparent border makes the box identical regardless of the variant.
* fix(web): widen the status column so DNS codes fit
The badge's padding and border pushed word-shaped DNS response codes such as NOERROR past the 60px cell, where the table's overflow rule clipped them.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(web): theme the sorted flow table header
The sorted column header and its chevron mask were the only colors left as literals after the semantic token layer landed, because LESS `lighten`/`fadeout` cannot operate on `var()`.
In dark mode this painted the sorted `th` near-white and hid the chevron, which is stroked in `--mitmweb-fg`, against its own mask.
Both now read `--mitmweb-th-sorted-bg`, with the mask deriving its alpha through `color-mix` instead.
The light value is byte-equal to what `lighten(#f2f2f2, 3%)` produced, so the light theme is unchanged.
No LESS color functions remain in the stylesheets, so the note about them in the token header comment goes away too.
* docs: add changelog entry for the sorted flow table header fix
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* feat(web): add dark theme and web_theme option
Add a `web_theme` mitmweb option (system/dark/light; default system) and apply it in the frontend by setting `data-theme` on the document root.
`system` follows the OS `prefers-color-scheme` and tracks live changes.
The dark palette overrides the semantic color tokens under `[data-theme="dark"]`, with targeted overrides for surfaces whose colors are consumed by LESS functions.
The CodeMirror content editor switches to the one-dark theme in dark mode.
Part of #7789.
* fix(web): theme native form controls for dark mode
Native selects, inputs, checkboxes and scrollbars ignored the semantic tokens and rendered with the browser's light defaults.
Set color-scheme per theme so they follow it, and replace the remaining literal black text colors on the local-applications input and the close-button hover states with tokens.
* fix(web): make search and close icons legible in dark mode
The search filter icon and the eventlog/flow-detail close icons were hardcoded to black/grey, which is hard to read on dark surfaces.
Route them through the foreground tokens instead.
* [autofix.ci] apply automated fixes
* fix(web): theme header and tab separators for dark mode
The header, nav-tab, and menu-group separator borders were computed with lighten(grey, ...), leaving them as bright literals that stayed light-grey on dark surfaces.
Route them through the border tokens instead so they follow the theme.
* feat(web): add a theme selector to the Options menu
The web_theme option was only reachable by scrolling to the bottom of the raw options list in the Edit Options modal.
Surface it as an Appearance dropdown (system/dark/light) in the header Options menu so the theme is discoverable and switchable in one click.
* refactor(web): drop the dark flow table rule override
The base branch tokenized every flow table row color, so the nested .flow-table tr block in the dark theme now duplicates rules that var() already resolves.
Override the --mitmweb-row-* tokens instead.
Base rows fall through to --mitmweb-bg / --mitmweb-bg-alt like they do in the light theme, rather than carrying their own near-identical literals.
* fix(web): give row hover a dark highlight color
--mitmweb-highlight kept its light value in dark mode, compositing a pale blue over the dark surface and leaving hovered header rows and command suggestions hard to read.
* fix(web): darken the first-line banner in dark mode
The request/response first line reused --mitmweb-accent, which is brightened for dark surfaces and left white monospace text at roughly 2.5:1 contrast.
Give the banner its own token pair so dark mode can use a deep blue with pale text without dragging the accent color along.
* fix(web): make status codes legible in dark mode
The status column painted itself with hardcoded CSS color names.
`darkgreen` and `darkred` are near-invisible against the dark surface, so route the colors through per-class tokens instead.
Light values are byte-equal to the literals they replace.
The shadow token added alongside them is consumed in the next commit.
* fix(web): stop footers casting a light halo in dark mode
Both footer shadows were hardcoded to a light gray, which on a dark surface reads as a thick white border rather than a shadow.
Route them through the shadow token so the dark theme casts black instead.
* fix(web): dim OS-supplied app icons in dark mode
The executable icons in the local-applications dropdown come from the OS as bitmaps, so we cannot recolor them.
macOS hands back a plain white square for the generic executable, which glares against the dark popover.
Dim them via a token so the white square reads as grey while colored app icons stay recognizable.
* fix(web): stop idle scrollbars showing as stray lines in dark mode
Panes such as the flow table reserve a scrollbar gutter permanently via `overflow-y: scroll`.
Under `color-scheme: dark` the UA paints that gutter light, so an idle scrollbar reads as a stray vertical line beside the pane divider rather than as a scrollbar.
Color the track to match the surface so it stays as invisible as it already is on light backgrounds.
* fix(web): blend the process-filter input into its dark header
Under color-scheme: dark the browser paints the native text field a lighter grey than the transparent dropdown header around it, so the input read lighter than the chevron beside it.
Making the field background transparent lets it blend with the header.
* fix(web): make the filter-docs row hover visible in dark mode
The dark highlight token was barely distinguishable from the popover surface, so a hovered suggestion row read as unhighlighted.
Brightening it makes the hovered row stand out.
The command-bar suggestion highlight shares this token and gains the same visibility.
* test(web): cover theme selector and status column colors
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* feat(web): add semantic CSS custom-property color layer
Introduce a :root set of semantic color tokens (surfaces, text, borders, accent, state) in global.less and route the recurring, directly-used colors across the LESS files through them.
Light-mode values are byte-equal to the literals they replace, so this is a visual no-op; it exists to give a future dark theme a single place to override.
Colors that LESS functions (lighten/darken/fadeout) consume, and one-off decorative colors, are intentionally left as literals because those functions cannot operate on var().
Part of #7789.
* [autofix.ci] apply automated fixes
* refactor(web): route remaining hardcoded colors through color tokens
Route the remaining directly-used background, text, and border literals across the LESS files through the existing semantic tokens introduced in the color layer, reusing tokens rather than adding new ones.
Light-mode colors stay visually unchanged; one-off decorative colors and LESS-function inputs are left as literals.
* refactor(web): tokenize recurring colors and drop dead styles
Add a --mitmweb-highlight token for the shared row-hover / suggestion-selection color (used in header.less and command.less) and a --mitmweb-gray token for the neutral gray reused across mode.less.
Remove the unused .command-suggestion rule and the dead rgba() border fallback in dropdown.less, which was overriding the tokenized border and rendering as translucent black.
* refactor(web): use the accent token for the first line and special tab
Replace the near-accent literals #428bca (flow detail first line) and #396cad (special tab) with --mitmweb-accent so the accent lives in exactly one place.
Both were within a few percent of the token, so this is a small deliberate light-theme shift rather than a visual no-op.
The special tab derived its hover from lighten(@special-color, 10%); since LESS functions cannot operate on var(), it now uses --mitmweb-accent-active, the same hover shade .btn-primary already uses.
* refactor(web): tokenize the mode green left border
Add a --mitmweb-green token for the #77c77a left border in mode.less, mirroring the --mitmweb-gray token already used by the neighbouring .gray-left-border.
* refactor(web): tokenize flow table row state colors
Add --mitmweb-row-* tokens for the highlighted, selected, and selected+highlighted row shades and route flowtable.less through them.
The base rows reuse the existing --mitmweb-bg / --mitmweb-bg-alt surfaces, whose values are byte-equal to the hsl() literals they replace.
Values are unchanged, so this is a visual no-op; it gives the dark theme one place to override the row states.
* docs: drop the changelog entry for the CSS color token layer
The entry conflicts with the upstream CHANGELOG.md on the PR.
Removing it for now; the entry will be re-added once the branch is rebased.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(web): avoid infinite componentDidUpdate loop in the event log
EventList.componentDidUpdate called this.onViewportUpdate() on every
update, even when the inputs to calcVScroll (the event list and the row
height) had not changed. onViewportUpdate reads the current scroll
position, recomputes vScroll, and setStates when the window shape
changes. Because the event log measures per-row heights, a setState it
produced could feed itself: rendering a new window remeasured rows,
which shifted the computed window, which setState again, so the cycle
never converged and surfaced as "Maximum update depth exceeded" while
scrolling the log.
Gate the call on prevProps.events !== this.props.events ||
prevProps.rowHeight !== this.props.rowHeight, mirroring the FlowTable fix
in #8233. The other call sites (componentDidMount, the resize listener,
the viewport onScroll, and setHeight when a row is first measured) still
drive updates as needed, so scrolling and autoscroll keep working.
Adds a regression test that fails without the gate.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* mitmweb: honor view_order_reversed for live flows
mitmweb only applied view_order_reversed to the initial flow list it
fetches over HTTP. Flows delivered afterwards over the WebSocket were
always appended to the end of the table, so with the option enabled the
newest flow stayed off-screen at the bottom instead of appearing on top.
Track the option in the flows reducer and, when no column sort is active,
insert newly added flows at the front when view_order_reversed is set and
at the back otherwise. This matches the order the server already uses for
the initial list.
Closes#5520
* [autofix.ci] apply automated fixes
* mitmweb: destructure orderReversed in flows reducer for consistency
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(web): preserve in-progress filter input across parent re-renders
`FilterInput`'s `onChange` only propagates valid filters to the parent,
so while the user is mid-typing an incomplete or invalid filter (e.g.
`~foo bar`) the local `state.value` intentionally diverges from
`props.value` — the parent still holds the last valid filter. Any
unrelated parent re-render then fires
`UNSAFE_componentWillReceiveProps`, which unconditionally called
`this.setState({ value: nextProps.value })` and clobbered the user's
in-progress text back to the last valid filter.
Gated the sync on `nextProps.value !== this.props.value` so the
component only adopts an externally-changed value. When the parent
re-renders without changing the value prop, the local state is
preserved.
Added a `FilterInputSpec.tsx` regression test alongside the existing
`should handle componentWillReceiveProps` test (which covers the
positive path — external value change still wins). The new test types
`~foo bar` (asserted invalid by the existing `should handle isValid`
test), rerenders with the same value prop, and asserts the in-progress
text survives. The test fails without the fix.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* fix(web): avoid infinite componentDidUpdate loop in FlowTable
`FlowTable.componentDidUpdate` calls `this.onViewportUpdate()` on every
update, even when the inputs to `calcVScroll` (the flow list and the row
height) did not change. `onViewportUpdate` reads the current
`viewport.scrollTop`, recomputes `vScroll`, and `setState`s when
`state.viewportTop !== scrollTop` or `vScroll` shape changed. The new
`state.viewportTop` is `Math.min(scrollTop, vScroll.end * rowHeight)`,
so when `vScroll.end * rowHeight < scrollTop` the new state stays
strictly below `scrollTop`, the inequality holds on the next iteration,
and the cycle spins until the browser kills the JS thread.
Gated the call on `prevProps.flowView !== this.props.flowView ||
prevProps.rowHeight !== this.props.rowHeight`. The other
`onViewportUpdate` call sites — `componentDidMount`, the window resize
listener, the viewport `onScroll` handler, and the post-scroll-into-view
call — are unchanged, so user scrolling and resizing still drive the
virtual-scroll window normally.
Added a `FlowTableSpec.tsx` regression test that spies on
`PureFlowTable.prototype.onViewportUpdate` and asserts it is NOT called
after a `select` dispatch (which changes connect-mapped props but leaves
`flowView` and `rowHeight` alone). The test fails without the fix.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* fix(web): render AVIF images and IANA icon favicons in the response tab
The `ViewImage` regex in `HttpMessage.tsx` rejected two valid image
content types, so the response tab did not render them inline:
1. `image/avif` was missing from the alternation. AVIF is widely served
by image CDNs and decoded natively in `<img>` by Chrome 85+,
Firefox 93+, and Safari 16.1+.
2. `image/vnd.microsoft.icon` (the IANA-canonical `.ico` MIME) never
matched because the alternation had `vnc.microsoft.icon` — a
`vnc`/`vnd` typo. The de-facto legacy `image/x-icon` continued to
work, which made the bug easy to miss. The dots are also unescaped,
so the regex coincidentally matches `image/vncXmicrosoftXicon` for
any single character `X`.
Added `avif`, corrected `vnc` → `vnd`, and escaped the dots. Added a
`ViewImage.matches` unit test covering both the additions and the
existing image MIME types.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* migrate bootstrap decoupling with dual classes and local less tokens
* migrate form and utility bootstrap classes to m-/u- styles
* remove remaining Bootstrap class aliases and migrate to internal m-* / u-* classes
* fix tab link colors to match previous blue styling
* update snapshots
* adjust table styling and alignment
* adjust divider style
* adjust flowdetails panel style
* adjust options modal style
* fix icons position and color in capture tab page
* adjust style search,highlight, intercept popovers
* update snapshots
* extract global styles into global.less
* keep behavior aligned with pre-migration Bootstrap (same class names)
* update snapshots
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* web: Reduce FlowTable Redux subscriptions from O(rows) to O(1)
Lift shared state (displayColumnNames, flowIndex) from per-row/per-cell
useAppSelector calls to the FlowTable parent component. This eliminates
N identical Redux subscriptions across all visible rows.
Changes:
- FlowTable.tsx: Fetch displayColumnNames from state.options.web_columns
in mapStateToProps; pass as prop to FlowRow along with flowIndex.
- FlowRow.tsx: Accept displayColumnNames and flowIndex as props instead
of useAppSelector. Remove unused useAppSelector import.
- FlowColumns.tsx: Accept optional flowIndex prop in index column;
use it instead of per-cell useAppSelector(_listIndex).
- FlowTableHead.tsx: Add null guard on displayColumnNames for
edge-case during initial state hydration.
- FlowRowSpec.tsx: Update test to pass new required props.
* [autofix.ci] apply automated fixes
* fix: pass flowIndex prop in FlowColumnsSpec snapshot test
The index column now uses flowIndex prop instead of
useAppSelector. Pass flowIndex={0} in the generic column
render loop so the snapshot matches the expected numeric
output.
* address review feedback: rename flowIndex to rowNumber, remove defensive guards
- Rename flowIndex → rowNumber per reviewer suggestion
- Make rowNumber required in FlowColumnProps (never undefined)
- Remove unnecessary || [] null guard in FlowTableHead
- Read displayColumnNames from test store instead of hardcoding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* fix: use list position (not view position) for # column
rowNumber now comes from _listIndex (insertion order) instead of
vScroll.start + idx (view position). This preserves the original
behavior where the # column shows the flow's arrival order regardless
of sorting or filtering.
Added test to verify index column displays rowNumber + 1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* docs: add CHANGELOG entry for FlowTable optimization
* [autofix.ci] apply automated fixes
---------
Co-authored-by: Ariel <ariel@captaintech.io>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* fix(web): allow clearing body content to empty in editor
The body editor's save function used `editedContent || content || ""`
which treats empty string as falsy, falling back to the original body
content. This makes it impossible to clear a body to empty via the
Edit button — the save always sends the original content.
Changed to `editedContent ?? content ?? ""` so that only `undefined`
(user didn't edit) falls back, while empty string is sent as-is.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* test: move body edit tests into HttpMessageSpec.tsx
Move the two body-edit tests from the separate HttpMessageEditSpec.tsx
into HttpMessageSpec.tsx as requested in review. The CodeEditor mock is
now file-scoped, which simplifies the edit-mode snapshot (CodeMirror DOM
replaced by a lightweight textarea stub).
* Update CHANGELOG.md
Co-authored-by: Matteo Luppi <100372313+lups2000@users.noreply.github.com>
* test: use conditional CodeEditor mock to preserve snapshot
Use a mockUseCodeEditor flag so the real CodeEditor renders by default
(keeping the existing snapshot unchanged) and only the body-edit tests
switch to the textarea mock. Rename CVD → cvd for consistency.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matteo Luppi <100372313+lups2000@users.noreply.github.com>
* Partially Revert "Web: harden `xsrf_token` usage (#7491)"
This reverts commit b761cb46c4.
The reason for this revert is that it's incompatible with Vite's
server (https://github.com/mitmproxy/mitmproxy/issues/7969).
We keep the parts that are compatible, and add an additional
`Sec-Fetch-Site` check for for all requests.
* use type imports for Vite compatibility
* make flow columns work with function name minification
* make modals work with function name minification
* vite: move assets
* web: switch builds to vite
* move to vite
* vite: move css and js
* update CHANGELOG
* [autofix.ci] apply automated fixes
* fix test failures
* fix static viewer
* obtain xsrf cookie lazily
* split js/css bundles into app/vendor
* [autofix.ci] apply automated fixes
* update compiled assets
* fix nits
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* feat: Make TCP inactivity timeout configurable (currently fixed at 10 minutes) #7909
* docs: updated CHANGELOG
* [autofix.ci] apply automated fixes
* fix: add tests
* fix: testcase
* fix nits:
- We currently only add command line flags for the most important options. `--set tcp_timeout=10` is the recommended approach for now.
- Move timeout to the "wait for close" part specifically.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <github@maximilianhils.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* remove tabs.isInitial
this was not used in the first place
* fix docs links
* fix headerlookup
WeakMap allows us to finally have proper cache invalidation
* store capture tab in url state
* Add new feature: storing of request and response streaming bodies
* Fix unit test by regenerating the options file with options_js.py
* Add test cases with store_streamed_bodies True and False
* Update Changelog
* Update Changelog with pull request ID
* [autofix.ci] apply automated fixes
* Update test with a dedicated test case for store_streamed_bodies
* Remove unnecessary option store_streamed_bodies from options.py
* Update docstrings for store_streamed_bodies and stream_large_bodies.
* Update docstring for store_streamed_bodies since it is not used anywhere in practice here.
* Fix typo in test_http.py
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>