feat: add auto-expand AI response groups setting#59
Conversation
|
Windows CI is failing, can you take a look? |
|
getting on my windows laptop now, and i'll give it a look. |
📝 WalkthroughWalkthroughThis change adds a new Changes
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/renderer/store/slices/sessionDetailSlice.ts (1)
589-610: Nit: Redundant reassignment on line 592.
oldGroupIdsis immediately assigned fromprevGroupIds, which is already aconstin scope. You can useprevGroupIdsdirectly in the filter predicate.♻️ Simplified diff
// Auto-expand newly arrived AI groups if the setting is enabled. // Uses prevGroupIds snapshotted before set() so the diff is accurate. if (get().appConfig?.general?.autoExpandAIGroups) { - const oldGroupIds = prevGroupIds; const newGroupIds = newConversation.items .filter( (item) => item.type === 'ai' && - !oldGroupIds.has((item as { type: 'ai'; group: { id: string } }).group.id) + !prevGroupIds.has((item as { type: 'ai'; group: { id: string } }).group.id) ) .map((item) => (item as { type: 'ai'; group: { id: string } }).group.id);The overall differential expansion logic is correct — it properly preserves user-collapsed groups while auto-expanding only newly arrived AI responses during live sessions. Based on learnings: "Use tabUISlice to maintain independent UI state per tab with expandedAIGroupIds... keyed by tabId to prevent UI state from one tab affecting another."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/store/slices/sessionDetailSlice.ts` around lines 589 - 610, Remove the redundant local assignment oldGroupIds = prevGroupIds and use prevGroupIds directly in the filter predicate; update the filter/map logic that computes newGroupIds to reference prevGroupIds instead of oldGroupIds so the behavior of the auto-expand block (get().appConfig?.general?.autoExpandAIGroups, latestAllTabs, expandAIGroupForTab) is unchanged but without the unnecessary reassignment.src/renderer/components/settings/sections/GeneralSection.tsx (1)
30-30: Consider narrowing the type to only boolean keys ofAppConfig['general'].The current signature accepts any key from
AppConfig['general'], butvalueis typed asboolean. SinceAppConfig['general']includes non-boolean properties (theme,defaultTab,claudeRootPath), this allows invalid calls likeonGeneralToggle('theme', true)to pass type checking.♻️ Proposed type-safe alternative
+type BooleanGeneralKeys = { + [K in keyof AppConfig['general']]: AppConfig['general'][K] extends boolean ? K : never; +}[keyof AppConfig['general']]; + interface GeneralSectionProps { readonly safeConfig: SafeConfig; readonly saving: boolean; - readonly onGeneralToggle: (key: keyof AppConfig['general'], value: boolean) => void; + readonly onGeneralToggle: (key: BooleanGeneralKeys, value: boolean) => void; readonly onThemeChange: (value: 'dark' | 'light' | 'system') => void; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/settings/sections/GeneralSection.tsx` at line 30, The onGeneralToggle signature accepts any key of AppConfig['general'] but the value is always boolean, allowing invalid calls for non-boolean fields; narrow the key type to only those keys whose AppConfig['general'] value is boolean by changing the type of the first parameter to the extracted boolean keys (e.g. use a conditional mapped type like { [K in keyof AppConfig['general']]: AppConfig['general'][K] extends boolean ? K : never }[keyof AppConfig['general']] or Extract<keyof AppConfig['general'], ... boolean check>), keeping the value as boolean so onGeneralToggle only accepts boolean properties of AppConfig['general'] (referencing onGeneralToggle and AppConfig['general']).
🤖 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/renderer/utils/sessionExporter.ts`:
- Around line 26-28: Replace the regex-based thousands separator in the return
expression (currently using
Math.round(n).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')) with a
loop-based formatter: compute rounded = Math.round(n), convert to string, handle
negative sign if present, then iterate characters from the end building a new
string and insert a comma every three digits before reversing/combining to
produce the final formatted string; update the return in sessionExporter.ts to
use this new loop-based routine so the function avoids the ReDoS-vulnerable
regex while preserving correct formatting and negatives.
---
Nitpick comments:
In `@src/renderer/components/settings/sections/GeneralSection.tsx`:
- Line 30: The onGeneralToggle signature accepts any key of AppConfig['general']
but the value is always boolean, allowing invalid calls for non-boolean fields;
narrow the key type to only those keys whose AppConfig['general'] value is
boolean by changing the type of the first parameter to the extracted boolean
keys (e.g. use a conditional mapped type like { [K in keyof
AppConfig['general']]: AppConfig['general'][K] extends boolean ? K : never
}[keyof AppConfig['general']] or Extract<keyof AppConfig['general'], ... boolean
check>), keeping the value as boolean so onGeneralToggle only accepts boolean
properties of AppConfig['general'] (referencing onGeneralToggle and
AppConfig['general']).
In `@src/renderer/store/slices/sessionDetailSlice.ts`:
- Around line 589-610: Remove the redundant local assignment oldGroupIds =
prevGroupIds and use prevGroupIds directly in the filter predicate; update the
filter/map logic that computes newGroupIds to reference prevGroupIds instead of
oldGroupIds so the behavior of the auto-expand block
(get().appConfig?.general?.autoExpandAIGroups, latestAllTabs,
expandAIGroupForTab) is unchanged but without the unnecessary reassignment.
5a3a86c to
93b515a
Compare
- Add toggle in settings to auto-expand AI response groups - Auto-expand new AI groups on live session refresh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/renderer/store/slices/sessionDetailSlice.ts (2)
566-572: Consider using a type guard for cleaner type narrowing.The filter already checks
item.type === 'ai', but the subsequent map still requires a type assertion. A small type guard could simplify this pattern throughout the file.♻️ Optional: Extract a type guard helper
+const isAIItem = (item: { type: string }): item is { type: 'ai'; group: { id: string } } => + item.type === 'ai'; + const prevGroupIds = new Set( (latestState.conversation?.items ?? []) - .filter((item) => item.type === 'ai') - .map((item) => (item as { type: 'ai'; group: { id: string } }).group.id) + .filter(isAIItem) + .map((item) => item.group.id) );As per coding guidelines: "Use TypeScript type guards with isXxx naming convention for runtime type checking."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/store/slices/sessionDetailSlice.ts` around lines 566 - 572, Introduce a TypeScript type guard named isAIItem (e.g., function isAIItem(item): item is { type: 'ai'; group: { id: string } }) and use it to replace the filter+type assertion when building prevGroupIds: call (latestState.conversation?.items ?? []).filter(isAIItem).map(i => i.group.id). Update any other similar patterns in this file to use the same isAIItem guard so you can remove the manual (item as ...) assertions and achieve cleaner type narrowing.
592-613: Redundant variable assignment on line 595.
oldGroupIdsis assigned directly fromprevGroupIdswithout modification. Consider usingprevGroupIdsdirectly to reduce noise.♻️ Suggested simplification
// Auto-expand newly arrived AI groups if the setting is enabled. // Uses prevGroupIds snapshotted before set() so the diff is accurate. if (get().appConfig?.general?.autoExpandAIGroups) { - const oldGroupIds = prevGroupIds; const newGroupIds = newConversation.items .filter( (item) => item.type === 'ai' && - !oldGroupIds.has((item as { type: 'ai'; group: { id: string } }).group.id) + !prevGroupIds.has((item as { type: 'ai'; group: { id: string } }).group.id) ) .map((item) => (item as { type: 'ai'; group: { id: string } }).group.id);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/store/slices/sessionDetailSlice.ts` around lines 592 - 613, The assignment oldGroupIds = prevGroupIds is redundant; update the code to use prevGroupIds directly in the filter (remove oldGroupIds variable) where newGroupIds is computed, keeping the rest of the logic unchanged (references: prevGroupIds, newConversation.items, latestAllTabs, expandAIGroupForTab, get().appConfig.general.autoExpandAIGroups). Ensure you only replace oldGroupIds with prevGroupIds in that block and remove the unused declaration.CHANGELOG.md (1)
11-12: Remove extra blank line for consistency.Two blank lines appear after the new entry, while the rest of the changelog uses single blank lines between entries.
🧹 Proposed fix
- `general.autoExpandAIGroups` setting: automatically expands all AI response groups when opening a transcript or when new AI responses arrive in a live session. Defaults to off. Stored in the on-disk config so it persists across restarts. - - Strict IPC input validation guards for project/session/subagent/search limits.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CHANGELOG.md` around lines 11 - 12, Remove the extra blank line after the new changelog entry in CHANGELOG.md so entries use a single blank line separator; locate the latest release/entry block (the newly added entry) and delete one of the two consecutive newline lines immediately following it to match the rest of the file's formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CHANGELOG.md`:
- Line 10: The CHANGELOG entry for the setting general.autoExpandAIGroups is
ambiguous about live session behavior; update the text so it clearly states that
when opening a transcript the setting expands all AI response groups, but in
live sessions it only auto-expands newly arriving AI response groups and does
not override groups the user has manually collapsed. Locate the
`general.autoExpandAIGroups` line in CHANGELOG.md and rewrite that sentence to
explicitly differentiate the two scenarios (transcript open vs live arrivals)
and note persistence in on-disk config.
---
Nitpick comments:
In `@CHANGELOG.md`:
- Around line 11-12: Remove the extra blank line after the new changelog entry
in CHANGELOG.md so entries use a single blank line separator; locate the latest
release/entry block (the newly added entry) and delete one of the two
consecutive newline lines immediately following it to match the rest of the
file's formatting.
In `@src/renderer/store/slices/sessionDetailSlice.ts`:
- Around line 566-572: Introduce a TypeScript type guard named isAIItem (e.g.,
function isAIItem(item): item is { type: 'ai'; group: { id: string } }) and use
it to replace the filter+type assertion when building prevGroupIds: call
(latestState.conversation?.items ?? []).filter(isAIItem).map(i => i.group.id).
Update any other similar patterns in this file to use the same isAIItem guard so
you can remove the manual (item as ...) assertions and achieve cleaner type
narrowing.
- Around line 592-613: The assignment oldGroupIds = prevGroupIds is redundant;
update the code to use prevGroupIds directly in the filter (remove oldGroupIds
variable) where newGroupIds is computed, keeping the rest of the logic unchanged
(references: prevGroupIds, newConversation.items, latestAllTabs,
expandAIGroupForTab, get().appConfig.general.autoExpandAIGroups). Ensure you
only replace oldGroupIds with prevGroupIds in that block and remove the unused
declaration.
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
| - `general.autoExpandAIGroups` setting: automatically expands all AI response groups when opening a transcript or when new AI responses arrive in a live session. Defaults to off. Stored in the on-disk config so it persists across restarts. |
There was a problem hiding this comment.
Clarify the live session behavior for accuracy.
The current wording "automatically expands all AI response groups when opening a transcript or when new AI responses arrive in a live session" could be misinterpreted to mean ALL groups expand in both scenarios. According to the PR objectives, in live sessions only newly arriving AI groups expand while preserving groups the user manually collapsed.
Consider rewording for clarity:
📝 Proposed rewording
-- `general.autoExpandAIGroups` setting: automatically expands all AI response groups when opening a transcript or when new AI responses arrive in a live session. Defaults to off. Stored in the on-disk config so it persists across restarts.
+- `general.autoExpandAIGroups` setting: automatically expands all AI response groups when opening a transcript. In live sessions, newly arriving AI responses expand automatically while preserving manually collapsed groups. Defaults to off. Stored in the on-disk config so it persists across restarts.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - `general.autoExpandAIGroups` setting: automatically expands all AI response groups when opening a transcript or when new AI responses arrive in a live session. Defaults to off. Stored in the on-disk config so it persists across restarts. | |
| - `general.autoExpandAIGroups` setting: automatically expands all AI response groups when opening a transcript. In live sessions, newly arriving AI responses expand automatically while preserving manually collapsed groups. Defaults to off. Stored in the on-disk config so it persists across restarts. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CHANGELOG.md` at line 10, The CHANGELOG entry for the setting
general.autoExpandAIGroups is ambiguous about live session behavior; update the
text so it clearly states that when opening a transcript the setting expands all
AI response groups, but in live sessions it only auto-expands newly arriving AI
response groups and does not override groups the user has manually collapsed.
Locate the `general.autoExpandAIGroups` line in CHANGELOG.md and rewrite that
sentence to explicitly differentiate the two scenarios (transcript open vs live
arrivals) and note persistence in on-disk config.
Summary
/clearbecause it lives in the on-disk config (~/.claude/claude-devtools-config.json)Changes
ConfigManager.ts/notifications.ts— addautoExpandAIGroups: booleantoGeneralConfigin both the main-process and shared type definitionsconfigValidation.ts— addautoExpandAIGroupsto the IPC allowlist with boolean validationsessionDetailSlice.ts— expand all groups on initial load; expand only newly arrived groups on live refresh (bug fixed: snapshot of old group IDs was taken afterset()overwrote state, causing the diff to always be empty)GeneralSection.tsx/SafeConfig/useSettingsHandlers.ts— wire up the toggle in Settingstest/main/ipc/configValidation.test.ts— two new cases covering the happy path and non-boolean rejectionCHANGELOG.md— entry under[Unreleased] > AddedTest plan
🤖 Generated with Claude Code
Summary by CodeRabbit