Skip to content

Comments

feat: configurable chat history scrollback#427

Merged
Dimillian merged 9 commits intoDimillian:mainfrom
amanthanvi:feat/chat-history-scrollback-setting
Feb 14, 2026
Merged

feat: configurable chat history scrollback#427
Dimillian merged 9 commits intoDimillian:mainfrom
amanthanvi:feat/chat-history-scrollback-setting

Conversation

@amanthanvi
Copy link
Contributor

Fixes #422

This is a reduced-scope follow-up to #423 (closed): it keeps the configurable scrollback setting + retention logic, but drops the conversation view virtualization to avoid the scroll-jump issue and reduce complexity.

What

  • Adds an app-wide setting for chat history scrollback (max items retained per thread).
  • Adds a Settings UI (unlimited toggle, presets, custom number input) with guidance about performance + re-syncing older messages.
  • Plumbs the setting into thread item retention logic (null = unlimited).

Notes

  • Increasing the scrollback limit does not automatically re-fetch older history that was previously trimmed; use "Sync from server" on a thread to restore older messages.
  • On startup, trimming is deferred until persisted settings hydrate (scrollback is treated as unlimited briefly), preventing older items from being dropped before the configured limit is known.
  • "Unlimited" may increase memory usage.

How to validate

  • Open Settings → Display & Sound → Chat and adjust scrollback (toggle Unlimited, pick presets, or enter a custom value).
  • Confirm longer threads retain more items locally.

Tests

  • npm run lint
  • npm run typecheck
  • npm run test
  • cd src-tauri && cargo test

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an app-wide “chat history scrollback” setting (number of conversation items retained per thread, with null meaning unlimited), exposes it in Settings UI, and wires it into thread item retention/trimming behavior across the app (including a deferred-trim behavior while settings hydrate).

Changes:

  • Make prepareThreadItems() accept an optional maxItemsPerThread (including unlimited via null) and update reducers to pass the configured limit.
  • Add chatHistoryScrollbackItems to app settings (frontend + Tauri) with normalization/defaulting, plus Settings UI controls (unlimited toggle, presets, custom input).
  • Add/extend unit + integration tests covering retention, trimming on config reduction, and the deferred-trim hydration flow.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/utils/threadItems.ts Adds configurable maxItemsPerThread behavior (null = unlimited) to thread item preparation/retention.
src/utils/threadItems.test.ts Adds tests for custom and unlimited scrollback behavior.
src/types.ts Extends AppSettings with chatHistoryScrollbackItems: number | null.
src/features/threads/hooks/useThreadsReducer.ts Adds maxItemsPerThread to thread state and a new reducer slice for config changes.
src/features/threads/hooks/useThreadsReducer.test.ts Tests trimming behavior when the max is reduced.
src/features/threads/hooks/useThreads.ts Plumbs the setting into the reducer initialization and updates via setMaxItemsPerThread.
src/features/threads/hooks/useThreads.integration.test.tsx Adds an integration test validating deferred trimming until the setting is applied.
src/features/threads/hooks/threadReducer/threadItemsSlice.ts Passes state.maxItemsPerThread into prepareThreadItems() for all relevant thread item updates.
src/features/threads/hooks/threadReducer/threadConfigSlice.ts New slice to normalize/apply setMaxItemsPerThread and trim existing thread items.
src/features/settings/hooks/useAppSettings.ts Adds defaults + normalization for chatHistoryScrollbackItems.
src/features/settings/components/sections/SettingsDisplaySection.tsx Adds Settings UI for chat scrollback (unlimited toggle, presets, custom input + guidance).
src/features/settings/components/sections/SettingsDisplaySection.test.tsx Adds UI tests for the new chat scrollback controls.
src/features/settings/components/SettingsView.test.tsx Updates settings fixtures to include the new setting.
src/App.tsx Passes scrollback setting into useThreads, using null while settings are loading to avoid premature trimming.
src-tauri/src/types.rs Adds the setting to the Tauri AppSettings struct with a default of Some(200) and a test assertion.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
});

it("applies scrollback limit before loading thread items", async () => {
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

This test name reads like the scrollback limit is applied before loading items, but the assertions actually verify the opposite (load unlimited when scrollbackItems is null, then trim after rerender to 200). Renaming the test to reflect the deferred-trimming behavior would make the intent clearer.

Suggested change
it("applies scrollback limit before loading thread items", async () => {
it("trims active items to scrollback limit after loading thread items", async () => {

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed the test to better match the deferred-trim behavior (defers trimming until scrollback settings hydrate). Fixed in 433fe25.

Comment on lines 4 to 11
function normalizeMaxItemsPerThread(value: number | null): number | null {
if (value === null) {
return null;
}
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
return 200;
}
return Math.floor(value);
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

normalizeMaxItemsPerThread hard-codes 200 as the fallback default. This same default is also defined in other places (e.g., threadItems + settings normalization), which makes it easy for defaults to drift over time. Consider importing a single shared constant (or exporting the threadItems default) and using it here.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call — replaced the hard-coded 200 with shared CHAT_SCROLLBACK_DEFAULT from src/utils/chatScrollback.ts. Fixed in 433fe25.

Comment on lines 24 to 26
nextItemsByThread[threadId] = prepareThreadItems(items, {
maxItemsPerThread: normalized,
});
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

setMaxItemsPerThread rebuilds every thread’s item array by calling prepareThreadItems for all entries in itemsByThread. Since reducer state already stores prepared items, this does extra normalization/truncation work and can be expensive on startup or with many threads. Consider trimming via a lightweight slice of the existing arrays (and only re-preparing when you detect unprepared/raw items).

Suggested change
nextItemsByThread[threadId] = prepareThreadItems(items, {
maxItemsPerThread: normalized,
});
const limit = normalized ?? items.length;
const startIndex = Math.max(items.length - limit, 0);
nextItemsByThread[threadId] = items.slice(startIndex);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated setMaxItemsPerThread to only slice(-limit) when the thread exceeds the new limit (and preserve references otherwise) instead of re-running prepareThreadItems for every thread. Fixed in 433fe25.

Comment on lines 25 to 47
const CHAT_SCROLLBACK_DEFAULT = 200;
const CHAT_SCROLLBACK_MIN = 50;
const CHAT_SCROLLBACK_MAX = 5000;

function normalizeChatHistoryScrollbackItems(value: unknown): number | null {
if (value === null) {
return null;
}
const parsed =
typeof value === "number"
? value
: typeof value === "string"
? Number(value)
: Number.NaN;
if (!Number.isFinite(parsed)) {
return CHAT_SCROLLBACK_DEFAULT;
}
const clamped = Math.min(
CHAT_SCROLLBACK_MAX,
Math.max(CHAT_SCROLLBACK_MIN, parsed),
);
return Math.round(clamped);
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The scrollback constants (default/min/max) are duplicated here and again in the Settings UI, and there’s also a separate default in the Tauri settings struct. This duplication increases the risk of inconsistent behavior (e.g., UI allows a value the settings normalizer clamps differently). Consider moving these constants + the normalization/clamp helper to a shared module and reusing it across the settings hook + settings UI (and ideally aligning the backend default too).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Centralized scrollback defaults/presets/clamping in src/utils/chatScrollback.ts and reused it from both useAppSettings and SettingsDisplaySection to avoid drift. Fixed in 433fe25 + ffd637f.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d99da41ba7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

value={scrollbackDraft}
disabled={scrollbackUnlimited}
onChange={(event) => setScrollbackDraft(event.target.value)}
onBlur={commitScrollback}

Choose a reason for hiding this comment

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

P1 Badge Prevent blur from saving intermediate scrollback values

When the max-items input loses focus, onBlur always calls commitScrollback, so clicking another control (for example the “Unlimited chat history” toggle or Reset) first persists the draft value before that click handler runs. In the common case where the draft is lower than the current limit, this transient update immediately trims thread items via setMaxItemsPerThread/prepareThreadItems (slice), and the subsequent higher/unlimited update cannot restore the dropped local history. This causes unintended data loss from a single click path and should avoid committing on blur when focus is moving to another scrollback control.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed — guarded the max-items onBlur so it does not commit when focus is moving to another scrollback control (toggle/preset/reset). Added data-scrollback-control markers + regression tests. Fixed in ffd637f.

@amanthanvi
Copy link
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. More of your lovely PRs please.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Dimillian Dimillian merged commit e53aea4 into Dimillian:main Feb 14, 2026
6 checks passed
rchybicki pushed a commit to rchybicki/CodexMonitor that referenced this pull request Feb 15, 2026
Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: configurable chat history scrollback (avoid losing older messages)

2 participants