Skip to content

refactor: consolidate formatter definitions (Phase 04)#819

Merged
reachrazamair merged 6 commits intoRunMaestro:rcfrom
jSydorowicz21:dedup/phase-04-formatter-consolidation
Apr 16, 2026
Merged

refactor: consolidate formatter definitions (Phase 04)#819
reachrazamair merged 6 commits intoRunMaestro:rcfrom
jSydorowicz21:dedup/phase-04-formatter-consolidation

Conversation

@jSydorowicz21
Copy link
Copy Markdown
Contributor

@jSydorowicz21 jSydorowicz21 commented Apr 12, 2026

Summary

Consolidates 43 duplicate formatter definitions across the codebase into canonical versions in src/shared/formatters.ts (uses existing file, does NOT create a new one).

Net: -422 lines across 51 files

04A - formatDuration (20 replaced)

Added 6 new canonical functions: formatDurationHuman, formatDurationCompact, formatDurationVerbose, formatDurationParts, formatDurationDecimal, formatTimestamp. Replaced local definitions in 10 UsageDashboard files, AboutModal, FirstRunCelebration, Toast, CueModal, mobile CuePanel, SymphonyModal, useContributorStats, cli/output, tabExport, groupChatExport.

04B - formatElapsedTime (5 replaced)

Replaced in: MergeProgressModal, MergeProgressOverlay, SummarizeProgressModal, SummarizeProgressOverlay, TransferProgressModal.

04C - formatTimestamp (13 replaced)

Canonical formatTimestamp(timestamp, style) with 4 styles (time, datetime, smart, full). Replaced in HistoryEntryItem, GroupChatHistoryPanel, MobileHistoryPanel, MessageHistory, GroupChatPanel, WizardMessageBubble, ConversationScreen, LongestAutoRunsTable, HistoryDetailModal, ResponseViewer, tabExport, groupChatExport.

04D - formatNumber / formatFileSize / estimateTokens / stripAnsi / generateId

  • formatNumber: 5 replaced in UsageDashboard
  • formatFileSize: 2 replaced with thin wrappers around formatSize
  • estimateTokens: 4 array-based defs replaced; tokenCounter.ts delegates
  • stripAnsi: main/utils/stripAnsi.ts re-exports from shared/stringUtils
  • generateId: main/stats/utils.ts re-exports from shared/uuid (skipped 4 domain-specific uses)

Test plan

  • npm run lint passes clean
  • 171/171 formatter tests pass
  • Usage Dashboard: costs, token counts, durations format correctly
  • History panel: timestamps render
  • Transfer/merge progress modals: elapsed time counter works
  • File sizes in file explorer correct
  • Auto Run document timestamps

Risk

Low to medium - formatters touch production code across main + renderer + cli + web. All canonical functions verified as strict supersets of the replaced versions. Note: canonical formatNumber uses uppercase K and toString() for small numbers (matching dashboard convention).

Summary by CodeRabbit

Release Notes

  • Refactor
    • Consolidated formatting utilities (timestamps, durations, numbers, file sizes, tokens) into shared modules for consistency across the application.
    • Updated number formatting to display integers for values under 1,000 and use uppercase "K" suffix for thousands.
    • Improved timestamp formatting with multiple display styles (time, datetime, smart, full).
    • Enhanced duration formatting with multiple variants for different contexts.
    • Standardized token estimation calculations.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b7f34071-b2da-4e01-af7f-7364bd41f024

📥 Commits

Reviewing files that changed from the base of the PR and between a46d031 and b24fe64.

📒 Files selected for processing (8)
  • src/__tests__/renderer/components/AgentSessionsBrowser.test.tsx
  • src/__tests__/renderer/components/FilePreview/filePreviewUtils.test.ts
  • src/__tests__/renderer/components/TransferProgressModal.test.tsx
  • src/__tests__/shared/formatters.test.ts
  • src/cli/output/formatter.ts
  • src/main/stats/utils.ts
  • src/main/utils/stripAnsi.ts
  • src/renderer/components/AboutModal.tsx
✅ Files skipped from review due to trivial changes (3)
  • src/renderer/components/AboutModal.tsx
  • src/tests/shared/formatters.test.ts
  • src/tests/renderer/components/AgentSessionsBrowser.test.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/tests/renderer/components/TransferProgressModal.test.tsx
  • src/tests/renderer/components/FilePreview/filePreviewUtils.test.ts
  • src/main/stats/utils.ts

📝 Walkthrough

Walkthrough

This PR consolidates duplicated formatting functions scattered across components into a centralized src/shared/formatters.ts module. Multiple duration, timestamp, file size, number, and token-estimation formatters are extracted and refactored. Components, hooks, and utilities across the renderer, CLI, and web layers are updated to import and use these shared implementations. Tests are adjusted accordingly.

Changes

Cohort / File(s) Summary
Core Shared Formatter Module
src/shared/formatters.ts
Added new exported formatters: formatDurationHuman, formatDurationCompact, formatDurationVerbose, formatDurationParts, formatDurationDecimal, formatTimestamp, and estimateTokensFromLogs. Modified formatNumber to return integers for values < 1000 and use uppercase "K" suffix for thousands instead of lowercase "k".
Test Updates
src/__tests__/renderer/components/AgentSessionsBrowser.test.tsx, src/__tests__/renderer/components/FilePreview/filePreviewUtils.test.ts, src/__tests__/renderer/components/TransferProgressModal.test.tsx, src/__tests__/shared/formatters.test.ts
Updated test expectations to reflect new formatter behavior: small numbers return integers instead of decimals, thousands use uppercase "K", time formatting uses milliseconds initially, file sizes use one-decimal precision.
Renderer Components - Duration Formatting
src/renderer/components/AboutModal.tsx, src/renderer/components/CueModal/cueModalUtils.ts, src/renderer/components/FirstRunCelebration.tsx, src/renderer/components/SymphonyModal.tsx, src/renderer/components/Toast.tsx, src/renderer/components/UsageDashboard/*
Replaced local formatDuration implementations with imported shared formatters (formatDurationHuman, formatDurationCompact, formatDurationVerbose, formatDurationParts).
Renderer Components - Timestamp Formatting
src/renderer/components/GroupChatHistoryPanel.tsx, src/renderer/components/GroupChatMessages.tsx, src/renderer/components/History/HistoryEntryItem.tsx, src/renderer/components/HistoryDetailModal.tsx, src/renderer/components/InlineWizard/WizardMessageBubble.tsx, src/renderer/components/ParticipantCard.tsx, src/renderer/components/Wizard/screens/ConversationScreen.tsx
Replaced local formatTime and formatTimestamp implementations with imported formatTimestamp from shared formatters with appropriate style parameters ('time', 'datetime', 'smart', 'full').
Renderer Components - Elapsed/Progress Time
src/renderer/components/MergeProgressOverlay.tsx, src/renderer/components/SummarizeProgressOverlay.tsx, src/renderer/components/TransferProgressModal.tsx
Removed local formatElapsedTime implementations and imported the shared formatElapsedTime formatter.
Renderer Components - Size & Token Estimation
src/renderer/components/FilePreview/filePreviewUtils.ts, src/renderer/components/MergeSessionModal.tsx, src/renderer/components/SendToAgentModal.tsx
Replaced local formatFileSize and estimateTokens implementations with imports from shared formatters (formatSize, estimateTokensFromLogs).
Renderer Hooks
src/renderer/hooks/agent/useMergeSession.ts, src/renderer/hooks/agent/useSendToAgent.ts, src/renderer/hooks/symphony/useContributorStats.ts
Replaced local token estimation and duration formatting helpers with imports from shared formatters.
Renderer Utilities
src/renderer/utils/documentStats.ts, src/renderer/utils/groupChatExport.ts, src/renderer/utils/tabExport.ts, src/renderer/utils/tokenCounter.ts
Delegated formatting logic (file size, duration, timestamp, token counting) to shared formatter imports.
Web Mobile Components
src/web/mobile/CuePanel.tsx, src/web/mobile/GroupChatPanel.tsx, src/web/mobile/MessageHistory.tsx, src/web/mobile/MobileHistoryPanel.tsx, src/web/mobile/ResponseViewer.tsx
Replaced local formatting implementations for duration, timestamp with shared formatters (formatElapsedTime, formatTimestamp, formatDurationCompact).
CLI & Main Process
src/cli/output/formatter.ts, src/main/stats/utils.ts, src/main/utils/stripAnsi.ts
Updated CLI formatter to use shared formatDurationDecimal, enhanced JSDoc for ID generation, and refactored stripAnsi to re-export shared implementation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Suggested reviewers

  • reachrazamair

Poem

🐰 Hark! The formatters do now converge,
No more scattered logic on the edge!
From components wide, their duplication fades,
One shared foundation now cascades,
In harmony, our codebase upgrades! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: consolidate formatter definitions (Phase 04)' accurately describes the main change—consolidating 43 duplicate formatter definitions into canonical implementations in src/shared/formatters.ts across 51 files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jSydorowicz21 jSydorowicz21 force-pushed the dedup/phase-04-formatter-consolidation branch from fa6c031 to 41ddab7 Compare April 12, 2026 23:19
@jSydorowicz21 jSydorowicz21 self-assigned this Apr 14, 2026
@jSydorowicz21 jSydorowicz21 marked this pull request as ready for review April 14, 2026 04:35
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 14, 2026

Greptile Summary

Consolidates 43 duplicate formatter definitions across 51 files into canonical functions in src/shared/formatters.ts, removing ~422 lines of duplicated code. All replaced implementations were verified as strict subsets of the new canonical versions; the only intentional behavioral differences are formatNumber returning integer strings for values under 1000 (previously "X.0") and uppercase K (previously lowercase k), and the canonical formatElapsedTime returning "0ms" for zero-ms inputs instead of "0s".

  • The seven new canonical functions (formatDurationHuman, formatDurationCompact, formatDurationVerbose, formatDurationParts, formatDurationDecimal, estimateTokensFromLogs, formatTimestamp) have no unit tests in the updated formatters.test.ts, leaving their edge-case behaviour unverified.
  • The formatNumber JSDoc @returns example still reads "1.5k" (lowercase) after the casing was changed to "1.5K".

Confidence Score: 5/5

  • Safe to merge — all remaining findings are P2 style and documentation issues that do not affect correctness.
  • All identified issues are P2: a stale JSDoc example, missing unit tests for new canonical functions, and a minor hour-formatting difference (numeric vs 2-digit) in one table. No logic bugs, data integrity issues, or breaking behavioral changes were found — the intentional output changes to formatNumber are well-documented and covered by updated tests.
  • src/tests/shared/formatters.test.ts — missing test coverage for the 7 new canonical functions added to src/shared/formatters.ts

Important Files Changed

Filename Overview
src/shared/formatters.ts Core canonical file: adds 6 duration formatters, estimateTokensFromLogs, and formatTimestamp; changes formatNumber to return integer strings for <1000 and uppercase K — JSDoc example still shows lowercase "k".
src/tests/shared/formatters.test.ts Updates formatNumber assertions from "X.0"/"k" to "X"/"K" but adds no tests for the 7 new canonical functions (formatDurationHuman, formatDurationCompact, formatDurationVerbose, formatDurationParts, formatDurationDecimal, estimateTokensFromLogs, formatTimestamp).
src/main/utils/stripAnsi.ts Replaced a 55-line bespoke SSH-sequence stripper with a re-export of stripAnsiCodes from shared/stringUtils; the shared implementation covers all the same patterns plus additional ]133; and ]7; sequences.
src/main/stats/utils.ts Swapped .substring(2, 11) for .slice(2, 11) — functionally identical for non-negative indices — and added documentation explaining why the domain-specific generateId format is kept instead of delegating to generateUUID.
src/renderer/components/MergeProgressModal.tsx Drops the local formatElapsedTime in favour of the canonical; the canonical additionally handles sub-second (returns "0ms" instead of "0s") and hours-range durations, which are improvements for a progress timer.
src/renderer/components/UsageDashboard/LongestAutoRunsTable.tsx Replaces local formatDuration and formatTime; formatTimestamp('time') uses hour:'2-digit' vs the old hour:'numeric', potentially adding a leading zero to single-digit hours in the table.
src/renderer/utils/tokenCounter.ts estimateTokens now delegates to estimateTokenCount; behaviour is identical for valid strings, and the new version additionally guards against null/undefined input.
src/cli/output/formatter.ts Local formatDuration aliased to the canonical formatDurationDecimal; logic is identical (ms/s/m/h with single decimal).
src/renderer/utils/documentStats.ts formatFileSize now wraps canonical formatSize with a negative-byte guard; output is equivalent for realistic non-negative inputs.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[src/shared/formatters.ts\nCanonical source] --> B[formatDurationHuman\nformatDurationCompact\nformatDurationVerbose\nformatDurationParts\nformatDurationDecimal]
    A --> C[formatTimestamp\nstyle: time / datetime / smart / full]
    A --> D[estimateTokensFromLogs]
    A --> E[formatNumber\nnum.toString for <1000\nuppercase K suffix]
    A --> F[formatElapsedTime\nalready existed]

    B --> G[UsageDashboard components\n10 files]
    B --> H[CueModal / SymphonyModal\nToast / FirstRunCelebration]
    B --> I[cli/output/formatter.ts\nformatDurationDecimal alias]
    B --> J[web/mobile CuePanel\nuseContributorStats]

    C --> K[LongestAutoRunsTable\nHistoryEntryItem\nGroupChatHistoryPanel\n+ 8 more]

    D --> L[tokenCounter.ts\ndocumentStats.ts delegate]

    E --> M[AgentSessionsBrowser\nUsageDashboard charts]

    F --> N[MergeProgressModal\nTransferProgressModal\nSummarizeProgressModal\n+ overlays]

    O[main/utils/stripAnsi.ts] -->|re-exports| P[shared/stringUtils\nstripAnsiCodes]
    Q[main/stats/utils.ts] -->|keeps domain-specific\ngenerateId format| R[Stats DB\nprimary/foreign keys]
Loading

Comments Outside Diff (2)

  1. src/shared/formatters.ts, line 43-46 (link)

    P2 Stale JSDoc example after kK change

    The @returns example still shows "1.5k" (lowercase), but the implementation now emits "1.5K" (uppercase). Readers or tooling that copy the example will get the wrong output.

  2. src/__tests__/shared/formatters.test.ts, line 6-19 (link)

    P2 No tests for the six new canonical functions

    formatDurationHuman, formatDurationCompact, formatDurationVerbose, formatDurationParts, formatDurationDecimal, estimateTokensFromLogs, and formatTimestamp are all now canonical exports, but none are imported or tested in this file. These functions replace production code across 20+ files, so missing coverage means edge-case regressions (e.g., ms = 0 in formatDurationVerbose returns "0 second", formatTimestamp timezone/locale sensitivity) won't be caught automatically.

Reviews (1): Last reviewed commit: "fix: restore generateId random segment t..." | Re-trigger Greptile

minute: '2-digit',
});
}
const formatTime = (timestamp: number) => formatTimestamp(timestamp, 'time');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 hour: '2-digit' vs hour: 'numeric' changes time display

The removed formatTime used hour: 'numeric' (no leading zero, e.g., "2:30 PM"), while formatTimestamp(timestamp, 'time') calls toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) — which emits "02:30 PM" in many en-US environments. The visual change is minor, but it differs from the old output and from how formatDate (still using hour: 'numeric') renders the same column. Worth double-checking the table renders as intended.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/shared/formatters.ts (1)

43-47: ⚠️ Potential issue | 🟡 Minor

Update formatNumber JSDoc examples to match output.

The docs still show lowercase k, while the function now returns uppercase K.

Also applies to: 49-53

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/shared/formatters.ts` around lines 43 - 47, The JSDoc for formatNumber
still shows lowercase "k" while the function returns uppercase "K"; update the
examples in the JSDoc for the formatNumber function (and any adjacent doc
comments that reference the output) to use "K" instead of "k" (e.g., "1.5K",
"2.3M", "1.0B") so the documentation matches the actual output of formatNumber.
🧹 Nitpick comments (3)
src/__tests__/renderer/components/FilePreview/filePreviewUtils.test.ts (1)

123-144: Add a regression test for negative byte handling.

Since formatFileSize now delegates to shared formatSize, it’s worth pinning the local bytes <= 0 => '0 B' behavior with an explicit negative-value test.

✅ Suggested test addition
 describe('formatFileSize', () => {
 	it('formats 0 bytes', () => {
 		expect(formatFileSize(0)).toBe('0 B');
 	});
+
+	it('clamps negative bytes to 0 B', () => {
+		expect(formatFileSize(-1)).toBe('0 B');
+	});

 	it('formats bytes', () => {
 		expect(formatFileSize(512)).toBe('512 B');
 	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/renderer/components/FilePreview/filePreviewUtils.test.ts`
around lines 123 - 144, Add a regression test that verifies formatFileSize
returns '0 B' for negative inputs; inside the describe('formatFileSize') block
add a new it('formats negative bytes', ...) that calls formatFileSize(-1) (and
optionally another negative value) and expects '0 B' to ensure the local bytes
<= 0 behavior is preserved now that formatFileSize delegates to formatSize.
src/renderer/components/SendToAgentModal.tsx (1)

24-25: Optional cleanup: remove the extra alias indirection.

You can alias directly in the import and drop the standalone constant.

♻️ Proposed simplification
-import { estimateTokensFromLogs } from '../../shared/formatters';
+import { estimateTokensFromLogs as estimateTokens } from '../../shared/formatters';
@@
-const estimateTokens = estimateTokensFromLogs;

Also applies to: 111-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/SendToAgentModal.tsx` around lines 24 - 25, The
import currently creates an extra alias indirection by importing getAgentIcon
then assigning it to a standalone constant; remove that intermediate constant
and alias directly in the import (e.g., import { getAgentIcon as agentIcon }
from '...') and update usages (e.g., any references to the standalone constant)
to use the direct import name (reference symbols: getAgentIcon,
estimateTokensFromLogs, SendToAgentModal component) so the extra indirection is
eliminated.
src/renderer/utils/tabExport.ts (1)

46-52: Use shared zero-duration output for the short-log fallback.

Line 47 still returns a hardcoded '0m', which can diverge from the shared compact formatter output style.

♻️ Suggested consistency tweak
 function formatDuration(logs: LogEntry[]): string {
-	if (logs.length < 2) return '0m';
+	if (logs.length < 2) return formatDurationCompact(0);

 	const firstTimestamp = logs[0].timestamp;
 	const lastTimestamp = logs[logs.length - 1].timestamp;
 	return formatDurationCompact(lastTimestamp - firstTimestamp);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/utils/tabExport.ts` around lines 46 - 52, The formatDuration
function currently returns a hardcoded '0m' for short logs which can diverge
from the shared compact formatter; change it to return the shared zero-duration
output by calling formatDurationCompact(0) instead of '0m' so formatDuration
uses the same formatting logic as formatDurationCompact (update within the
formatDuration function referencing logs, firstTimestamp, lastTimestamp and
formatDurationCompact).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/shared/formatters.ts`:
- Around line 384-387: The token estimate in estimateTokensFromLogs currently
uses Math.round which can undercount; change the calculation to use Math.ceil
for the division (i.e., replace Math.round(totalChars / 4) with
Math.ceil(totalChars / 4)) so non-empty logs never yield 0 and to align behavior
with estimateTokenCount; update the return expression in estimateTokensFromLogs
to use Math.ceil and keep the same totalChars computation.

---

Outside diff comments:
In `@src/shared/formatters.ts`:
- Around line 43-47: The JSDoc for formatNumber still shows lowercase "k" while
the function returns uppercase "K"; update the examples in the JSDoc for the
formatNumber function (and any adjacent doc comments that reference the output)
to use "K" instead of "k" (e.g., "1.5K", "2.3M", "1.0B") so the documentation
matches the actual output of formatNumber.

---

Nitpick comments:
In `@src/__tests__/renderer/components/FilePreview/filePreviewUtils.test.ts`:
- Around line 123-144: Add a regression test that verifies formatFileSize
returns '0 B' for negative inputs; inside the describe('formatFileSize') block
add a new it('formats negative bytes', ...) that calls formatFileSize(-1) (and
optionally another negative value) and expects '0 B' to ensure the local bytes
<= 0 behavior is preserved now that formatFileSize delegates to formatSize.

In `@src/renderer/components/SendToAgentModal.tsx`:
- Around line 24-25: The import currently creates an extra alias indirection by
importing getAgentIcon then assigning it to a standalone constant; remove that
intermediate constant and alias directly in the import (e.g., import {
getAgentIcon as agentIcon } from '...') and update usages (e.g., any references
to the standalone constant) to use the direct import name (reference symbols:
getAgentIcon, estimateTokensFromLogs, SendToAgentModal component) so the extra
indirection is eliminated.

In `@src/renderer/utils/tabExport.ts`:
- Around line 46-52: The formatDuration function currently returns a hardcoded
'0m' for short logs which can diverge from the shared compact formatter; change
it to return the shared zero-duration output by calling formatDurationCompact(0)
instead of '0m' so formatDuration uses the same formatting logic as
formatDurationCompact (update within the formatDuration function referencing
logs, firstTimestamp, lastTimestamp and formatDurationCompact).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 143e3a97-409f-420e-a8a8-777c680d78b9

📥 Commits

Reviewing files that changed from the base of the PR and between 36b677a and 3198338.

📒 Files selected for processing (52)
  • src/__tests__/renderer/components/AgentSessionsBrowser.test.tsx
  • src/__tests__/renderer/components/FilePreview/filePreviewUtils.test.ts
  • src/__tests__/renderer/components/TransferProgressModal.test.tsx
  • src/__tests__/shared/formatters.test.ts
  • src/cli/output/formatter.ts
  • src/main/stats/utils.ts
  • src/main/utils/stripAnsi.ts
  • src/renderer/components/AboutModal.tsx
  • src/renderer/components/CueModal/cueModalUtils.ts
  • src/renderer/components/FilePreview/filePreviewUtils.ts
  • src/renderer/components/FirstRunCelebration.tsx
  • src/renderer/components/GroupChatHistoryPanel.tsx
  • src/renderer/components/GroupChatMessages.tsx
  • src/renderer/components/History/HistoryEntryItem.tsx
  • src/renderer/components/HistoryDetailModal.tsx
  • src/renderer/components/InlineWizard/WizardMessageBubble.tsx
  • src/renderer/components/MergeProgressModal.tsx
  • src/renderer/components/MergeProgressOverlay.tsx
  • src/renderer/components/MergeSessionModal.tsx
  • src/renderer/components/ParticipantCard.tsx
  • src/renderer/components/SendToAgentModal.tsx
  • src/renderer/components/SummarizeProgressModal.tsx
  • src/renderer/components/SummarizeProgressOverlay.tsx
  • src/renderer/components/SymphonyModal.tsx
  • src/renderer/components/Toast.tsx
  • src/renderer/components/TransferProgressModal.tsx
  • src/renderer/components/UsageDashboard/ActivityHeatmap.tsx
  • src/renderer/components/UsageDashboard/AgentComparisonChart.tsx
  • src/renderer/components/UsageDashboard/AgentEfficiencyChart.tsx
  • src/renderer/components/UsageDashboard/AgentUsageChart.tsx
  • src/renderer/components/UsageDashboard/AutoRunStats.tsx
  • src/renderer/components/UsageDashboard/DurationTrendsChart.tsx
  • src/renderer/components/UsageDashboard/LocationDistributionChart.tsx
  • src/renderer/components/UsageDashboard/LongestAutoRunsTable.tsx
  • src/renderer/components/UsageDashboard/PeakHoursChart.tsx
  • src/renderer/components/UsageDashboard/SourceDistributionChart.tsx
  • src/renderer/components/UsageDashboard/SummaryCards.tsx
  • src/renderer/components/UsageDashboard/WeekdayComparisonChart.tsx
  • src/renderer/components/Wizard/screens/ConversationScreen.tsx
  • src/renderer/hooks/agent/useMergeSession.ts
  • src/renderer/hooks/agent/useSendToAgent.ts
  • src/renderer/hooks/symphony/useContributorStats.ts
  • src/renderer/utils/documentStats.ts
  • src/renderer/utils/groupChatExport.ts
  • src/renderer/utils/tabExport.ts
  • src/renderer/utils/tokenCounter.ts
  • src/shared/formatters.ts
  • src/web/mobile/CuePanel.tsx
  • src/web/mobile/GroupChatPanel.tsx
  • src/web/mobile/MessageHistory.tsx
  • src/web/mobile/MobileHistoryPanel.tsx
  • src/web/mobile/ResponseViewer.tsx

Comment thread src/shared/formatters.ts
@nehaaprasad
Copy link
Copy Markdown
Contributor

AI Code Trust — ship readiness

Score: 83 · Verdict: RISKY · Model: deterministic-v1

Notable issues found; review before shipping.

Top issues:

  • maintainability (medium) — File is very long (3024 lines); consider splitting. src/__tests__/renderer/components/AgentSessionsBrowser.test.tsx:1
  • maintainability (medium) — File is very long (574 lines); consider splitting. src/__tests__/renderer/components/TransferProgressModal.test.tsx:1
  • maintainability (medium) — File is very long (425 lines); consider splitting. src/__tests__/shared/formatters.test.ts:1
  • maintainability (medium) — File is very long (733 lines); consider splitting. src/cli/output/formatter.ts:1
  • performance (low) — useEffect with an empty dependency array runs once — confirm dependencies are complete. src/renderer/components/AboutModal.tsx:57
  • accessibility (low) — onClick on non-button elements should include keyboard support and role. src/renderer/components/AboutModal.tsx:120
  • maintainability (medium) — File is very long (456 lines); consider splitting. src/renderer/components/AboutModal.tsx:1
  • maintainability (low) — Very long line reduces readability. src/renderer/components/AboutModal.tsx:137
  • performance (low) — useEffect with an empty dependency array runs once — confirm dependencies are complete. src/renderer/components/FirstRunCelebration.tsx:156
  • accessibility (low) — onClick on non-button elements should include keyboard support and role. src/renderer/components/FirstRunCelebration.tsx:221
  • maintainability (medium) — File is very long (473 lines); consider splitting. src/renderer/components/FirstRunCelebration.tsx:1
  • performance (low) — useEffect with an empty dependency array runs once — confirm dependencies are complete. src/renderer/components/GroupChatHistoryPanel.tsx:184

@jSydorowicz21
Copy link
Copy Markdown
Contributor Author

AI Code Trust — ship readiness

Score: 83 · Verdict: RISKY · Model: deterministic-v1

Notable issues found; review before shipping.

Top issues:

  • maintainability (medium) — File is very long (3024 lines); consider splitting. src/__tests__/renderer/components/AgentSessionsBrowser.test.tsx:1
  • maintainability (medium) — File is very long (574 lines); consider splitting. src/__tests__/renderer/components/TransferProgressModal.test.tsx:1
  • maintainability (medium) — File is very long (425 lines); consider splitting. src/__tests__/shared/formatters.test.ts:1
  • maintainability (medium) — File is very long (733 lines); consider splitting. src/cli/output/formatter.ts:1
  • performance (low) — useEffect with an empty dependency array runs once — confirm dependencies are complete. src/renderer/components/AboutModal.tsx:57
  • accessibility (low) — onClick on non-button elements should include keyboard support and role. src/renderer/components/AboutModal.tsx:120
  • maintainability (medium) — File is very long (456 lines); consider splitting. src/renderer/components/AboutModal.tsx:1
  • maintainability (low) — Very long line reduces readability. src/renderer/components/AboutModal.tsx:137
  • performance (low) — useEffect with an empty dependency array runs once — confirm dependencies are complete. src/renderer/components/FirstRunCelebration.tsx:156
  • accessibility (low) — onClick on non-button elements should include keyboard support and role. src/renderer/components/FirstRunCelebration.tsx:221
  • maintainability (medium) — File is very long (473 lines); consider splitting. src/renderer/components/FirstRunCelebration.tsx:1
  • performance (low) — useEffect with an empty dependency array runs once — confirm dependencies are complete. src/renderer/components/GroupChatHistoryPanel.tsx:184

Hey Neha, a good bit of this feedback is out of scope for this pr and a bit off topic :p

@jSydorowicz21 jSydorowicz21 added refactor Clean-up needs ready to merge This PR is ready to merge labels Apr 15, 2026
@nehaaprasad
Copy link
Copy Markdown
Contributor

Thanks for the feedback that makes sense.

I understand that some of my comments were broader than the scope of this PR. My intention was to point out a few improvements I noticed, but I agree they should be tracked separately if they are not directly related to the change here.

I’ll keep future review comments more PR-scoped and focused only on the current change.

@jSydorowicz21 thanks again your feedback actually helped me identify a useful improvement feature to implement in my product. :)

jSydorowicz21 and others added 6 commits April 16, 2026 12:37
Phase 04A - formatDuration:
- Added 6 new canonical variants to shared/formatters.ts: formatDurationHuman,
  formatDurationCompact, formatDurationVerbose, formatDurationParts,
  formatDurationDecimal, estimateTokensFromLogs, formatTimestamp
- Replaced 20 local formatDuration definitions across UsageDashboard (10),
  AboutModal, FirstRunCelebration, Toast, CueModal, CuePanel, SymphonyModal,
  useContributorStats, cli/formatter, tabExport, groupChatExport
- Updated canonical formatNumber to use uppercase K and toString() for <1000

Phase 04B - formatElapsedTime:
- Replaced 5 local formatElapsedTime definitions in MergeProgressModal,
  MergeProgressOverlay, SummarizeProgressModal, SummarizeProgressOverlay,
  TransferProgressModal with import from shared/formatters

Phase 04C - formatTimestamp:
- Added canonical formatTimestamp(timestamp, style) with 4 styles:
  time, datetime, smart (default), full
- Replaced 13 local formatTime/formatTimestamp definitions across
  HistoryEntryItem, GroupChatHistoryPanel, MobileHistoryPanel, MessageHistory,
  GroupChatPanel (smart); WizardMessageBubble, ConversationScreen,
  LongestAutoRunsTable (time); HistoryDetailModal, ResponseViewer (datetime);
  tabExport, groupChatExport (full)
- GroupChatMessages and ParticipantCard refactored to delegate string
  formatting to shared formatTimestamp
- ThinkingStatusPill skipped (duration formatter, not timestamp)

Phase 04D - formatNumber, formatFileSize, estimateTokens, stripAnsi, generateId:
- Replaced 5 local formatNumber in UsageDashboard components
- Replaced 2 formatFileSize (documentStats, filePreviewUtils) with
  thin wrappers around shared formatSize
- Replaced 4 estimateTokens/estimateTokensFromLogs (MergeSessionModal,
  SendToAgentModal, useSendToAgent, useMergeSession); tokenCounter.ts
  now delegates to shared estimateTokenCount
- Replaced main/utils/stripAnsi.ts with re-export from shared/stringUtils
- Replaced main/stats/utils.ts generateId with re-export from shared/uuid
- Skipped symphony.ts formatNumber (different function: toLocaleString)
- Skipped useLayerStack/useBatchedSessionUpdates/useCommandHistory/
  useOfflineQueue generateId (domain-specific ID formats with prefixes)
- Skipped contextExtractor.ts estimateTokenCount (different signature)
main/stats/utils.ts generateId was incorrectly replaced with
generateUUID in Phase 04. The stats DB depends on the
timestamp-random format (<timestamp>-<random>) as a load-bearing
invariant - primary keys, foreign keys, and backward compatibility
with existing persisted stats rely on it.

Restore the inline timestamp-random generator with a comment warning
against future replacement.
…tNumber

The shared formatNumber in src/shared/formatters.ts was updated in this
phase to use uppercase K and omit the decimal for small numbers (matching
dashboard convention). AgentSessionsBrowser.test.tsx still used the old
'8.0k' / '700.0' expectations. Update three assertions to match the new
canonical output:

- '700.0' -> '700' (no decimal for sub-thousand)
- '8.0k' -> '8.0K' (uppercase suffix)

This is the intended UI consistency improvement - users will now see
8.0K everywhere instead of mixed k/K across components.
The migration from .substring(2, 11) to .slice(2, 10) silently
shortened the random segment from 9 to 8 characters. This is a
load-bearing invariant for stats DB primary/foreign keys.
- estimateTokensFromLogs now uses Math.ceil consistent with estimateTokenCount
- formatTimestamp uses hour: 'numeric' to match previous formatTime behavior
@jSydorowicz21 jSydorowicz21 force-pushed the dedup/phase-04-formatter-consolidation branch from a46d031 to b24fe64 Compare April 16, 2026 17:38
@reachrazamair reachrazamair merged commit d4bc26b into RunMaestro:rc Apr 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready to merge This PR is ready to merge refactor Clean-up needs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants