Skip to content

Commit 8bb6d30

Browse files
committed
improvement(mothership): address review — hook props interface, text-sm token, behavior-only tests
1 parent 0766ff6 commit 8bb6d30

5 files changed

Lines changed: 50 additions & 41 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-title.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ export function useToolCallTitle(tool: ToolCallTitleInput | undefined): ToolCall
6161
[toolName, streamingArgs]
6262
)
6363
const isCountingDown = toolName === WaitTool.id && tool?.status === 'executing'
64-
const elapsedMs = useElapsedMs(isCountingDown, tool?.startedAt, tool?.toolCallId)
64+
const elapsedMs = useElapsedMs({
65+
active: isCountingDown,
66+
startedAt: tool?.startedAt,
67+
resetKey: tool?.toolCallId,
68+
})
6569
if (!tool) return undefined
6670
const liveTitle = isCountingDown
6771
? getWaitCountdownTitle(tool.params, elapsedMs)

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/pending-tag-indicator.tsx‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,23 @@ interface PendingTagIndicatorProps {
1212
waitingSince?: number
1313
}
1414

15+
interface ElapsedCountProps {
16+
since?: number
17+
}
18+
1519
/**
1620
* The wait's elapsed time, ticking in its own component so the loader beside it
1721
* never re-renders on the clock. Hidden from assistive tech: the loader's status
1822
* already announces the wait, and a count changing every second would be noise.
1923
*/
20-
function ElapsedCount({ since }: { since: number | undefined }) {
21-
const elapsedMs = useElapsedMs(since !== undefined, since)
24+
function ElapsedCount({ since }: ElapsedCountProps) {
25+
const elapsedMs = useElapsedMs({ active: since !== undefined, startedAt: since })
2226
if (elapsedMs < ELAPSED_VISIBLE_AFTER_MS) return null
2327
return (
2428
<span
2529
aria-hidden='true'
26-
className='animate-stream-fade-in text-[14px] text-[var(--text-muted)] tabular-nums'
30+
data-wait-elapsed
31+
className='animate-stream-fade-in text-[var(--text-muted)] text-sm tabular-nums'
2732
>
2833
{formatDuration(elapsedMs)}
2934
</span>

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.dom.test.tsx‎

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,14 @@ function advance(ms: number) {
5050
})
5151
}
5252

53-
/** The elapsed count is the only `aria-hidden` span whose text is a duration. */
54-
function elapsedText(): string | null {
55-
const spans = container.querySelectorAll('span[aria-hidden="true"]')
56-
for (const span of spans) {
57-
if (/^\d+(m \d+)?s$/.test(span.textContent ?? '')) return span.textContent
58-
}
59-
return null
53+
function hasElapsedCount(): boolean {
54+
return container.querySelector('[data-wait-elapsed]') !== null
6055
}
6156

62-
function isThinkingShown(): boolean {
63-
return container.querySelector('output')?.closest('[aria-hidden="false"]') !== null
57+
/** The loader's status sits in the tail slot, which is exposed only while the indicator shows. */
58+
function isIndicatorExposed(): boolean {
59+
const status = container.querySelector('output')
60+
return status !== null && status.closest('[aria-hidden="false"]') !== null
6461
}
6562

6663
beforeEach(() => {
@@ -82,51 +79,47 @@ afterEach(() => {
8279
})
8380

8481
describe('turn wait indicator', () => {
85-
it('shows Thinking at send and adds the elapsed time once the wait runs long', () => {
82+
it('shows from send and adds the elapsed count only once the wait runs long', () => {
8683
renderTurn([], true)
8784

88-
expect(isThinkingShown()).toBe(true)
89-
expect(container.textContent).toContain('Thinking')
90-
expect(elapsedText()).toBeNull()
85+
expect(isIndicatorExposed()).toBe(true)
86+
expect(hasElapsedCount()).toBe(false)
9187

9288
advance(ELAPSED_VISIBLE_AFTER_MS - 1_000)
93-
expect(elapsedText()).toBeNull()
89+
expect(hasElapsedCount()).toBe(false)
9490

9591
advance(1_000)
96-
expect(elapsedText()).toBe('5s')
97-
98-
advance(7_000)
99-
expect(elapsedText()).toBe('12s')
92+
expect(hasElapsedCount()).toBe(true)
10093
})
10194

10295
it('keeps counting through hidden reasoning, which is not visible output', () => {
10396
renderTurn([], true)
104-
advance(6_000)
97+
advance(ELAPSED_VISIBLE_AFTER_MS - 1_000)
10598

10699
renderTurn([THINKING_BLOCK], true)
107100
advance(1_000)
108101

109-
expect(isThinkingShown()).toBe(true)
110-
expect(elapsedText()).toBe('7s')
102+
expect(isIndicatorExposed()).toBe(true)
103+
expect(hasElapsedCount()).toBe(true)
111104
})
112105

113-
it('hides the indicator and its count when the first output arrives', () => {
106+
it('hides the indicator and drops the count when the first output arrives', () => {
114107
renderTurn([], true)
115108
advance(8_000)
116-
expect(elapsedText()).toBe('8s')
109+
expect(hasElapsedCount()).toBe(true)
117110

118111
renderTurn([THINKING_BLOCK, TEXT_BLOCK], true)
119112

120-
expect(isThinkingShown()).toBe(false)
121-
expect(elapsedText()).toBeNull()
113+
expect(isIndicatorExposed()).toBe(false)
114+
expect(hasElapsedCount()).toBe(false)
122115
})
123116

124117
it('does not count while a running tool row owns the wait', () => {
125118
renderTurn([RUNNING_TOOL_BLOCK], true)
126119
advance(10_000)
127120

128-
expect(isThinkingShown()).toBe(false)
129-
expect(elapsedText()).toBeNull()
121+
expect(isIndicatorExposed()).toBe(false)
122+
expect(hasElapsedCount()).toBe(false)
130123
expect(vi.getTimerCount()).toBe(0)
131124
})
132125

@@ -137,19 +130,19 @@ describe('turn wait indicator', () => {
137130
renderTurn([], false)
138131

139132
expect(container.querySelector('output')).toBeNull()
140-
expect(elapsedText()).toBeNull()
133+
expect(hasElapsedCount()).toBe(false)
141134
expect(vi.getTimerCount()).toBe(0)
142135
})
143136

144137
it('removes the indicator and stops ticking when the user stops the turn', () => {
145138
renderTurn([], true)
146139
advance(8_000)
147140

148-
renderTurn([STOPPED_BLOCK], true)
141+
renderTurn([STOPPED_BLOCK], false)
149142

150143
expect(container.querySelector('output')).toBeNull()
151-
expect(container.textContent).toContain('Stopped by user')
152-
expect(elapsedText()).toBeNull()
144+
expect(hasElapsedCount()).toBe(false)
145+
expect(vi.getTimerCount()).toBe(0)
153146
})
154147

155148
it('clears its clock on unmount', () => {

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,7 @@ function MessageContentInner({
10201020
* wait. A ref, not state — every stream chunk moves it, and it is only read on
10211021
* the renders that show the shimmer.
10221022
*/
1023-
const waitStartedAtRef = useRef(Date.now())
1023+
const waitStartedAtRef = useRef<number>(Date.now())
10241024

10251025
const segments = useMemo<MessageSegment[]>(
10261026
() =>

‎apps/sim/app/workspace/[workspaceId]/home/hooks/use-elapsed-ms.ts‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,18 @@ const ELAPSED_TICK_MS = 250
1515
* no start to give. A sample taken under a different `resetKey` reads as 0, so
1616
* switching subjects never flashes the previous subject's count.
1717
*/
18-
export function useElapsedMs(
19-
active: boolean,
20-
startedAt: number | undefined,
21-
resetKey: string | number | undefined = startedAt
22-
): number {
18+
interface UseElapsedMsProps {
19+
active: boolean
20+
startedAt?: number
21+
/** Identity of what is being timed; defaults to `startedAt`. */
22+
resetKey?: string | number
23+
}
24+
25+
export function useElapsedMs({
26+
active,
27+
startedAt,
28+
resetKey = startedAt,
29+
}: UseElapsedMsProps): number {
2330
const [sample, setSample] = useState({ resetKey, elapsedMs: 0 })
2431

2532
useEffect(() => {

0 commit comments

Comments
 (0)