From a26f926755fa9a1747e8725ba424594421374593 Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Fri, 8 Aug 2025 16:57:11 +0800 Subject: [PATCH 01/10] Add visibleViewColumns setting to uiStore --- src/model/ui/ui-store.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/model/ui/ui-store.ts b/src/model/ui/ui-store.ts index 607eb89b..2149430e 100644 --- a/src/model/ui/ui-store.ts +++ b/src/model/ui/ui-store.ts @@ -180,10 +180,26 @@ export class UiStore { @persist @observable private _themeName: ThemeName | 'automatic' | 'custom' = 'automatic'; + @persist('map') @observable + private _visibleViewColumns = new Map([ + ['Timestamp', false], + ['Method', true], + ['Status', true], + ['Source', true], + ['Host', true], + ['PathAndQuery', true], + ['Duration', true], + ['Size', true], + ]); //TODO: implement a strict type + get themeName() { return this._themeName; } + get visibleViewColumns() { + return this._visibleViewColumns + }; + /** * Stores if user prefers a dark color theme (for example when set in system settings). * Used if automatic theme is enabled. From 8a701174173f8ae27945cc1fea446fff29b65287 Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Fri, 8 Aug 2025 16:57:27 +0800 Subject: [PATCH 02/10] Add getHeaderContextMenuCallback to ViewEventContextMenuBuilder --- .../view/view-context-menu-builder.ts | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/components/view/view-context-menu-builder.ts b/src/components/view/view-context-menu-builder.ts index fb95aaf6..309ba209 100644 --- a/src/components/view/view-context-menu-builder.ts +++ b/src/components/view/view-context-menu-builder.ts @@ -27,8 +27,10 @@ export class ViewEventContextMenuBuilder { private onPin: (event: CollectedEvent) => void, private onDelete: (event: CollectedEvent) => void, private onBuildRuleFromExchange: (exchange: HttpExchangeView) => void, - private onPrepareToResendRequest?: (exchange: HttpExchangeView) => void - ) {} + private onPrepareToResendRequest?: (exchange: HttpExchangeView) => void, + private onHeaderColumnOptionChange?: (columnName: string, show: boolean) => void, + ) { + } private readonly BaseOptions = { Pin: { @@ -142,4 +144,28 @@ export class ViewEventContextMenuBuilder { }; } + getHeaderContextMenuCallback(enabledColumns: Map) { + let menuOptions: ContextMenuItem[] = []; + + enabledColumns.forEach((enabled, columnName) => { + menuOptions.push({ + type: 'option', + label: (!enabled ? "Show " : "Hide ") + columnName, + callback: () => { + this.onHeaderColumnOptionChange ? this.onHeaderColumnOptionChange(columnName, !enabled) : console.log('onHeaderColumnOptionChange callback not set'); + } + }); + }); + + return (mouseEvent: React.MouseEvent) => { + const sortedOptions = _.sortBy(menuOptions, (o: ContextMenuItem) => + o.type === 'separator' || !(o.enabled ?? true) + ) as Array>; + + this.uiStore.handleContextMenuEvent( + mouseEvent, + sortedOptions + ) + }; + } } \ No newline at end of file From 6e2af0ac4ea39caeb5001e7ac5a7540f351e953a Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Fri, 8 Aug 2025 17:13:05 +0800 Subject: [PATCH 03/10] Add Timestamp column to ViewEventList and fix formatting --- src/components/view/view-event-list.tsx | 155 ++++++++++++++---------- 1 file changed, 91 insertions(+), 64 deletions(-) diff --git a/src/components/view/view-event-list.tsx b/src/components/view/view-event-list.tsx index e17da91e..b18fdbb3 100644 --- a/src/components/view/view-event-list.tsx +++ b/src/components/view/view-event-list.tsx @@ -7,6 +7,7 @@ import AutoSizer from 'react-virtualized-auto-sizer'; import { FixedSizeList as List, ListChildComponentProps } from 'react-window'; import { styled } from '../../styles' +import { css } from "styled-components"; import { ArrowIcon, Icon, PhosphorIcon, WarningIcon } from '../../icons'; import { @@ -86,7 +87,7 @@ const ListContainer = styled.div<{ role: 'table' }>` } `; -const Column = styled.div<{ role: 'cell' | 'columnheader' }>` +const columnStyles = css` display: block; overflow: hidden; text-overflow: ellipsis; @@ -94,6 +95,10 @@ const Column = styled.div<{ role: 'cell' | 'columnheader' }>` padding: 3px 0; `; +const Column = styled.div<{ role: 'cell' | 'columnheader' }>` + ${columnStyles} +`; + const RowPin = styled( filterProps(Icon, 'pinned') ).attrs((p: { pinned: boolean }) => ({ @@ -147,6 +152,26 @@ const MarkerHeader = styled.div<{ role: 'columnheader' }>` flex-shrink: 0; `; +const BaseTimestamp = ({ timestamp, role = 'cell', className, children }: { + timestamp?: number, + role?: 'columnheader' | 'cell', + className?: string, + children?: React.ReactNode +}) => { + return ( +
+ {timestamp != null ? (new Date(timestamp).toLocaleTimeString()) : (children ?? '-')} +
+ ); +}; + +const Timestamp = styled(BaseTimestamp)` + ${columnStyles}; + transition: flex-basis 0.1s; + flex-shrink: 0; + flex-grow: 0; +`; + const Method = styled(Column)` transition: flex-basis 0.1s; ${(p: { pinned?: boolean }) => @@ -434,30 +459,31 @@ const ExchangeRow = inject('uiStore')(observer(({ className={isSelected ? 'selected' : ''} style={style} > - + - { request.method } + + {request.method} { response === 'aborted' ? - : exchange.downstream.isBreakpointed - ? - : exchange.isWebSocket() && response?.statusCode === 101 - ? - : + : exchange.downstream.isBreakpointed + ? + : exchange.isWebSocket() && response?.statusCode === 101 + ? + : } @@ -473,21 +499,21 @@ const ExchangeRow = inject('uiStore')(observer(({ ) && } - - { request.parsedUrl.host } + + {request.parsedUrl.host} - - { request.parsedUrl.pathname + request.parsedUrl.search } + + {request.parsedUrl.pathname + request.parsedUrl.search} ; })); @@ -801,6 +827,7 @@ export class ViewEventList extends React.Component { return + Timestamp} Method Status Source @@ -810,41 +837,41 @@ export class ViewEventList extends React.Component { { events.length === 0 - ? (isPaused - ? - Interception is paused, resume it to collect intercepted requests - - : - Connect a client and intercept some requests, and they'll appear here - - ) - - : filteredEvents.length === 0 - ? - No requests match this search filter{ - isPaused ? ' and interception is paused' : '' - } - - - : {({ height, width }) => - {() => - - { EventRow } - - } - } + ? (isPaused + ? + Interception is paused, resume it to collect intercepted requests + + : + Connect a client and intercept some requests, and they'll appear here + + ) + + : filteredEvents.length === 0 + ? + No requests match this search filter{ + isPaused ? ' and interception is paused' : '' + } + + + : {({ height, width }) => + {() => + + {EventRow} + + } + } } ; } From dbd98c94a5847312a5c2ae3feb3953885975677d Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Fri, 8 Aug 2025 17:18:20 +0800 Subject: [PATCH 04/10] Add Duration column to ViewEventList --- src/components/common/duration-pill.tsx | 43 ++-------------------- src/components/view/view-event-list.tsx | 30 ++++++++++++++++ src/util/utils.ts | 48 +++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 src/util/utils.ts diff --git a/src/components/common/duration-pill.tsx b/src/components/common/duration-pill.tsx index 9d07e0ba..73599ed2 100644 --- a/src/components/common/duration-pill.tsx +++ b/src/components/common/duration-pill.tsx @@ -5,47 +5,10 @@ import { TimingEvents } from '../../types'; import { observableClock } from '../../util/observable'; import { Pill } from './pill'; +import { calculateAndFormatDuration, FormattedDurationProps } from "../../util/utils"; -function sigFig(num: number, figs: number): string { - return num.toFixed(figs); -} - -type DurationPillProps = { className?: string } & ( - | { durationMs: number } - | { timingEvents: Partial } -); - -const calculateDuration = (timingEvents: Partial) => { - const doneTimestamp = timingEvents.responseSentTimestamp ?? timingEvents.abortedTimestamp; - - if (timingEvents.startTimestamp !== undefined && doneTimestamp !== undefined) { - return doneTimestamp - timingEvents.startTimestamp; - } - - if (timingEvents.startTime !== undefined) { - // This may not be perfect - note that startTime comes from the server so we might be - // mildly out of sync (ehhhh, in theory) but this is only for pending requests where - // that's unlikely to be an issue - the final time will be correct regardless. - return observableClock.getTime() - timingEvents.startTime; - } -} +type DurationPillProps = { className?: string } & FormattedDurationProps; export const DurationPill = observer((p: DurationPillProps) => { - let duration: number | undefined; - - if ('durationMs' in p) { - duration = p.durationMs; - } else if (p.timingEvents) { - duration = calculateDuration(p.timingEvents); - } - - if (duration === undefined) return null; - - return { - duration < 100 ? sigFig(duration, 1) + 'ms' : // 22.3ms - duration < 1000 ? sigFig(duration, 0) + 'ms' : // 999ms - duration < 5000 ? sigFig(duration / 1000, 2) + ' seconds' : // 3.04 seconds - duration < 9900 ? sigFig(duration / 1000, 1) + ' seconds' : // 8.2 seconds - sigFig(duration / 1000, 0) + ' seconds' // 30 seconds - }; + return {calculateAndFormatDuration(p)}; }); \ No newline at end of file diff --git a/src/components/view/view-event-list.tsx b/src/components/view/view-event-list.tsx index b18fdbb3..8a80fd81 100644 --- a/src/components/view/view-event-list.tsx +++ b/src/components/view/view-event-list.tsx @@ -27,6 +27,7 @@ import { } from '../../model/events/categorization'; import { nameStepClass } from '../../model/rules/rule-descriptions'; import { getReadableSize } from '../../util/buffer'; +import { calculateAndFormatDuration } from "../../util/utils"; import { UnreachableCheck } from '../../util/error'; import { filterProps } from '../component-utils'; @@ -212,6 +213,33 @@ const PathAndQuery = styled(Column)` flex-basis: 1000px; `; +const BaseDuration = observer(({ exchange, role = 'cell', className, children }: { + exchange?: HttpExchange, role?: 'columnheader' | 'cell', + className?: string, + children?: React.ReactNode +}) => { + let duration: string | null | undefined; + if (exchange != null) { + duration = calculateAndFormatDuration({ timingEvents: exchange.timingEvents }); + } + return ( +
+ {duration ? + duration : + (children ?? 'Duration') + } +
+ ); +}); + +const Duration = styled(BaseDuration)` + ${columnStyles}; + flex-basis: 71px; + flex-shrink: 0; + flex-grow: 0; +`; + + // Match Method + Status, but shrink right margin slightly so that // spinner + "WebRTC Media" fits OK. const EventTypeColumn = styled(Column)` @@ -515,6 +543,7 @@ const ExchangeRow = inject('uiStore')(observer(({ {request.parsedUrl.pathname + request.parsedUrl.search} + ; })); @@ -833,6 +862,7 @@ export class ViewEventList extends React.Component { Source Host Path and query + Duration { diff --git a/src/util/utils.ts b/src/util/utils.ts new file mode 100644 index 00000000..723a9997 --- /dev/null +++ b/src/util/utils.ts @@ -0,0 +1,48 @@ +import { TimingEvents } from "../types"; +import { observableClock } from "./observable"; + +export type FormattedDurationProps = + { + durationMs?: number, + timingEvents: Partial + }; + +export const calculateDuration = (timingEvents: Partial): number | undefined => { + const doneTimestamp = timingEvents.responseSentTimestamp ?? timingEvents.abortedTimestamp ?? timingEvents.wsClosedTimestamp; + + if (timingEvents.startTimestamp !== undefined && doneTimestamp !== undefined) { + return doneTimestamp - timingEvents.startTimestamp; + } + + if (timingEvents.startTime !== undefined) { + // This may not be perfect - note that startTime comes from the server so we might be + // mildly out of sync (ehhhh, in theory) but this is only for pending requests where + // that's unlikely to be an issue - the final time will be correct regardless. + return observableClock.getTime() - timingEvents.startTime; + } +} + +export const calculateAndFormatDuration = (props: FormattedDurationProps): string | null | undefined => { + let duration: number | undefined; + + if (props.durationMs !== undefined) { + duration = props.durationMs; + } else if (props.timingEvents) { + duration = calculateDuration(props.timingEvents); + } + + if (duration === undefined) + return null; + + return ( + duration < 100 ? sigFig(duration, 1) + 'ms' : // 22.3ms + duration < 1000 ? sigFig(duration, 0) + 'ms' : // 999ms + duration < 5000 ? sigFig(duration / 1000, 2) + 's' : // 3.04s + duration < 59000 ? sigFig(duration / 1000, 1) + 's' : // 30.2s + sigFig(duration / 60000, 1) + 'm' // 1.1m + ); +}; + +function sigFig(num: number, figs: number): string { + return num.toFixed(figs); +} \ No newline at end of file From 02d16c6f839a2a72e1f8e27abf8823941cc34b8d Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Fri, 8 Aug 2025 17:28:51 +0800 Subject: [PATCH 05/10] Add header context menu - ViewEventList --- .../view/view-context-menu-builder.ts | 6 ++--- src/components/view/view-event-list.tsx | 7 ++++-- src/components/view/view-page.tsx | 22 ++++++++++++------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/components/view/view-context-menu-builder.ts b/src/components/view/view-context-menu-builder.ts index 309ba209..e165ce1d 100644 --- a/src/components/view/view-context-menu-builder.ts +++ b/src/components/view/view-context-menu-builder.ts @@ -28,7 +28,7 @@ export class ViewEventContextMenuBuilder { private onDelete: (event: CollectedEvent) => void, private onBuildRuleFromExchange: (exchange: HttpExchangeView) => void, private onPrepareToResendRequest?: (exchange: HttpExchangeView) => void, - private onHeaderColumnOptionChange?: (columnName: string, show: boolean) => void, + private onHeaderColumnOptionChange?: (visibleViewColumns: Map, columnName: string, show: boolean) => void, ) { } @@ -144,7 +144,7 @@ export class ViewEventContextMenuBuilder { }; } - getHeaderContextMenuCallback(enabledColumns: Map) { + getHeaderToggleContextMenu(enabledColumns: Map) { let menuOptions: ContextMenuItem[] = []; enabledColumns.forEach((enabled, columnName) => { @@ -152,7 +152,7 @@ export class ViewEventContextMenuBuilder { type: 'option', label: (!enabled ? "Show " : "Hide ") + columnName, callback: () => { - this.onHeaderColumnOptionChange ? this.onHeaderColumnOptionChange(columnName, !enabled) : console.log('onHeaderColumnOptionChange callback not set'); + this.onHeaderColumnOptionChange ? this.onHeaderColumnOptionChange(enabledColumns, columnName, !enabled) : console.log('onHeaderColumnOptionChange callback not set'); } }); }); diff --git a/src/components/view/view-event-list.tsx b/src/components/view/view-event-list.tsx index 8a80fd81..ad8d9ee1 100644 --- a/src/components/view/view-event-list.tsx +++ b/src/components/view/view-event-list.tsx @@ -854,9 +854,12 @@ export class ViewEventList extends React.Component { const { events, filteredEvents, isPaused } = this.props; return - + - Timestamp} + Timestamp Method Status Source diff --git a/src/components/view/view-page.tsx b/src/components/view/view-page.tsx index cbb4d4f7..5eed706b 100644 --- a/src/components/view/view-page.tsx +++ b/src/components/view/view-page.tsx @@ -225,13 +225,13 @@ class ViewPage extends React.Component { return contentPerspective === 'client' ? this.selectedEvent.downstream - : contentPerspective === 'server' - ? (this.selectedEvent.upstream ?? this.selectedEvent.downstream) - : contentPerspective === 'original' - ? this.selectedEvent.original - : contentPerspective === 'transformed' - ? this.selectedEvent.transformed - : unreachableCheck(contentPerspective) + : contentPerspective === 'server' + ? (this.selectedEvent.upstream ?? this.selectedEvent.downstream) + : contentPerspective === 'original' + ? this.selectedEvent.original + : contentPerspective === 'transformed' + ? this.selectedEvent.transformed + : unreachableCheck(contentPerspective) } private readonly contextMenuBuilder = new ViewEventContextMenuBuilder( @@ -240,7 +240,8 @@ class ViewPage extends React.Component { this.onPin, this.onDelete, this.onBuildRuleFromExchange, - this.onPrepareToResendRequest + this.onPrepareToResendRequest, + this.onHeaderColumnOptionChange ); componentDidMount() { @@ -558,6 +559,11 @@ class ViewPage extends React.Component { navigate(`/send`); } + @action.bound + async onHeaderColumnOptionChange(visibleViewColumns: Map, columnName: string, show: boolean) { + visibleViewColumns.set(columnName, show); + } + @action.bound onDelete(event: CollectedEvent) { const { filteredEvents } = this.filteredEventState; From 3bd35c51c0d693592d883854b874f23b73102404 Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Fri, 8 Aug 2025 17:43:03 +0800 Subject: [PATCH 06/10] Add timestamp and duration to rest of the lists --- src/components/view/view-event-list.tsx | 83 ++++++++++++++----------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/components/view/view-event-list.tsx b/src/components/view/view-event-list.tsx index ad8d9ee1..27d9a875 100644 --- a/src/components/view/view-event-list.tsx +++ b/src/components/view/view-event-list.tsx @@ -116,12 +116,12 @@ const RowPin = styled( ${(p: { pinned: boolean }) => p.pinned - ? ` + ? ` width: auto; padding: 8px 7px; && { margin-right: -3px; } ` - : ` + : ` padding: 8px 0; width: 0 !important; margin: 0 !important; @@ -177,8 +177,8 @@ const Method = styled(Column)` transition: flex-basis 0.1s; ${(p: { pinned?: boolean }) => p.pinned - ? 'flex-basis: 50px;' - : 'flex-basis: 71px;' + ? 'flex-basis: 50px;' + : 'flex-basis: 71px;' } flex-shrink: 0; @@ -246,8 +246,8 @@ const EventTypeColumn = styled(Column)` transition: flex-basis 0.1s; ${(p: { pinned?: boolean }) => p.pinned - ? 'flex-basis: 109px;' - : 'flex-basis: 130px;' + ? 'flex-basis: 109px;' + : 'flex-basis: 130px;' } margin-right: 6px !important; @@ -299,6 +299,7 @@ const EventListRow = styled.div<{ role: 'row' }>` color: ${p => p.theme.highlightColor}; fill: ${p => p.theme.highlightColor}; + * { /* Override status etc colours to ensure contrast & give row max visibility */ color: ${p => p.theme.highlightColor}; @@ -331,13 +332,7 @@ const TrafficEventListRow = styled(EventListRow)` const TlsListRow = styled(EventListRow)` height: 28px !important; /* Important required to override react-window's style attr */ - margin: 2px 0; - - font-style: italic; - justify-content: center; - text-align: center; - - opacity: 0.7; + margin: 2px 0 2px 20px; &:hover { opacity: 1; @@ -350,6 +345,13 @@ const TlsListRow = styled(EventListRow)` } `; +const TlsRowDescription = styled(Column)` + flex-grow: 1; + font-style: italic; + justify-content: center; + text-align: center; + opacity: 0.7; +`; export const TableHeaderRow = styled.div<{ role: 'row' }>` height: 38px; overflow: hidden; @@ -588,8 +590,9 @@ const RTCConnectionRow = observer(({ className={isSelected ? 'selected' : ''} style={style} > - + + //TODO: Expose timingEvents { !event.closeState && } WebRTC @@ -656,8 +659,9 @@ const RTCStreamRow = observer(({ className={isSelected ? 'selected' : ''} style={style} > - + + //TODO: Expose timingEvents { !event.closeState && } WebRTC { event.isRTCDataChannel() @@ -746,10 +750,11 @@ const BuiltInApiRow = observer((p: { className={p.isSelected ? 'selected' : ''} style={p.style} > - + + - { api.service.shortName }: { apiOperationName } + {api.service.shortName}: {apiOperationName} - { apiRequestDescription } + {apiRequestDescription} + }); @@ -783,24 +789,29 @@ const TlsRow = observer((p: { const connectionTarget = tlsEvent.upstreamHostname || 'unknown domain'; - return - { - tlsEvent.isTlsTunnel() && - tlsEvent.isOpen() && - - } { - description - } connection to { connectionTarget } - + return ( + + + + { + tlsEvent.isTlsTunnel() && + tlsEvent.isOpen() && + + } { + description + } connection to {connectionTarget} + + + ); }); @observer From cb0231ee697cf2cc7f9b86f0aa180fe9e8309e3c Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Fri, 8 Aug 2025 18:20:01 +0800 Subject: [PATCH 07/10] Add ColumnVisibilityToggle --- src/components/view/view-event-list.tsx | 42 +++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/components/view/view-event-list.tsx b/src/components/view/view-event-list.tsx index 27d9a875..3b4c56e9 100644 --- a/src/components/view/view-event-list.tsx +++ b/src/components/view/view-event-list.tsx @@ -100,6 +100,26 @@ const Column = styled.div<{ role: 'cell' | 'columnheader' }>` ${columnStyles} `; +const ColumnVisibilityToggle = inject('uiStore')(observer(({ columnName, uiStore, children }: { + columnName: string, + uiStore?: UiStore, + children?: React.ReactNode +}) => { + //Render by default + let renderComponent = true; + + if (uiStore) { + const { visibleViewColumns } = uiStore; + renderComponent = visibleViewColumns.get(columnName) === true + } + + return ( + <> + {renderComponent && (children ?? children)} + + ) +})); + const RowPin = styled( filterProps(Icon, 'pinned') ).attrs((p: { pinned: boolean }) => ({ @@ -160,9 +180,11 @@ const BaseTimestamp = ({ timestamp, role = 'cell', className, children }: { children?: React.ReactNode }) => { return ( -
- {timestamp != null ? (new Date(timestamp).toLocaleTimeString()) : (children ?? '-')} -
+ +
+ {timestamp != null ? (new Date(timestamp).toLocaleTimeString()) : (children ?? '-')} +
+
); }; @@ -223,12 +245,14 @@ const BaseDuration = observer(({ exchange, role = 'cell', className, children }: duration = calculateAndFormatDuration({ timingEvents: exchange.timingEvents }); } return ( -
- {duration ? - duration : - (children ?? 'Duration') - } -
+ +
+ {duration ? + duration : + (children ?? 'Duration') + } +
+
); }); From fe2aee482dfbdf32b607a95c2597c5228fad5f92 Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Mon, 25 Aug 2025 21:31:19 +0800 Subject: [PATCH 08/10] Remove size column temporarily --- src/model/ui/ui-store.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/model/ui/ui-store.ts b/src/model/ui/ui-store.ts index 2149430e..247a7b67 100644 --- a/src/model/ui/ui-store.ts +++ b/src/model/ui/ui-store.ts @@ -188,8 +188,7 @@ export class UiStore { ['Source', true], ['Host', true], ['PathAndQuery', true], - ['Duration', true], - ['Size', true], + ['Duration', true] ]); //TODO: implement a strict type get themeName() { From 3789ee237463234e43dd7df08ca776fdb4acc582 Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Mon, 25 Aug 2025 21:45:29 +0800 Subject: [PATCH 09/10] Fix conflicts --- src/util/text.ts | 8 ++++---- src/util/utils.ts | 15 +++------------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/util/text.ts b/src/util/text.ts index e5768b0d..442c493f 100644 --- a/src/util/text.ts +++ b/src/util/text.ts @@ -34,7 +34,7 @@ const sigFig = (num: number, figs: number): string => export const formatDuration = (duration: number) => duration < 100 ? sigFig(duration, 1) + 'ms' : // 22.3ms - duration < 1000 ? sigFig(duration, 0) + 'ms' : // 999ms - duration < 5000 ? sigFig(duration / 1000, 2) + ' seconds' : // 3.04 seconds - duration < 9900 ? sigFig(duration / 1000, 1) + ' seconds' : // 8.2 seconds - sigFig(duration / 1000, 0) + ' seconds'; // 30 seconds \ No newline at end of file + duration < 1000 ? sigFig(duration, 0) + 'ms' : // 999ms + duration < 5000 ? sigFig(duration / 1000, 2) + 's' : // 3.04s + duration < 59000 ? sigFig(duration / 1000, 1) + 's' : // 30.2s + sigFig(duration / 60000, 1) + 'm' // 1.1m \ No newline at end of file diff --git a/src/util/utils.ts b/src/util/utils.ts index 723a9997..0b06bf5d 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -1,5 +1,6 @@ import { TimingEvents } from "../types"; import { observableClock } from "./observable"; +import {formatDuration} from "./text"; export type FormattedDurationProps = { @@ -34,15 +35,5 @@ export const calculateAndFormatDuration = (props: FormattedDurationProps): strin if (duration === undefined) return null; - return ( - duration < 100 ? sigFig(duration, 1) + 'ms' : // 22.3ms - duration < 1000 ? sigFig(duration, 0) + 'ms' : // 999ms - duration < 5000 ? sigFig(duration / 1000, 2) + 's' : // 3.04s - duration < 59000 ? sigFig(duration / 1000, 1) + 's' : // 30.2s - sigFig(duration / 60000, 1) + 'm' // 1.1m - ); -}; - -function sigFig(num: number, figs: number): string { - return num.toFixed(figs); -} \ No newline at end of file + return formatDuration(duration); +}; \ No newline at end of file From e6d27219b09e8b4c760946905797ae6ff4b4b67a Mon Sep 17 00:00:00 2001 From: Sugavanas Date: Tue, 26 Aug 2025 22:50:35 +0800 Subject: [PATCH 10/10] Update ui-store.ts - Remove default columns from list temporarily --- src/model/ui/ui-store.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/model/ui/ui-store.ts b/src/model/ui/ui-store.ts index ea2bcacd..9ba60247 100644 --- a/src/model/ui/ui-store.ts +++ b/src/model/ui/ui-store.ts @@ -186,11 +186,6 @@ export class UiStore { @persist('map') @observable private _visibleViewColumns = new Map([ ['Timestamp', false], - ['Method', true], - ['Status', true], - ['Source', true], - ['Host', true], - ['PathAndQuery', true], ['Duration', true] ]); //TODO: implement a strict type