From 053a6d238df36a5fc4472019e9a0099455b6bc04 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Mon, 10 Aug 2026 03:12:02 +0200 Subject: [PATCH] fix(web): adopt #263's fetch ceiling, persisted sort and numeric collation @jordan8037310 opened #263 against the same two issues while this branch was in flight. Three details there are better than what this had, so they are folded in with credit: - the Resume list pulls 200 unified sessions instead of 60, so the filter can reach a real backlog rather than stopping at an arbitrary ceiling (the endpoint clamps at 500), - the sort choice persists per device in localStorage, like `codeman:skin` and the other display keys that stay out of the synced schema, - alphabetical sorts collate with `{sensitivity:'base', numeric:true}`, so w2- sorts before w10- and case never splits one project's rows apart. Co-Authored-By: Claude Opus 5 (1M context) --- src/web/public/terminal-ui.js | 48 +++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/web/public/terminal-ui.js b/src/web/public/terminal-ui.js index d28c0bc7..0b9f4218 100644 --- a/src/web/public/terminal-ui.js +++ b/src/web/public/terminal-ui.js @@ -1956,8 +1956,16 @@ Object.assign(CodemanApp.prototype, { /** Number of history items shown before "Show More" */ _HISTORY_INITIAL_COUNT: 10, - /** How many past sessions the home screen loads (also the filter/sort corpus). */ - _HISTORY_FETCH_LIMIT: 60, + /** + * How many past sessions the home screen loads (also the filter/sort corpus). + * 200, not the old 60, so the filter can reach a real backlog — an install with + * 35+ conversations would otherwise hit the ceiling before the filter is useful + * (raised in @jordan8037310's #263; the endpoint clamps at 500). + */ + _HISTORY_FETCH_LIMIT: 200, + + /** localStorage key for the per-device sort choice (#263). */ + _HISTORY_SORT_KEY: 'codeman:historySort', async loadHistorySessions() { const container = document.getElementById('historySessions'); @@ -1995,7 +2003,12 @@ Object.assign(CodemanApp.prototype, { } }, - /** Wire the filter box and sort select once; both re-render from the cached corpus. */ + /** + * Wire the filter box and sort select once; both re-render from the cached + * corpus. The sort choice is restored from (and saved to) localStorage — it is + * a per-device display preference, so it stays out of the synced settings + * schema, same as `codeman:skin`. + */ _wireHistoryControls() { if (this._historyControlsWired) return; const filter = document.getElementById('historyFilter'); @@ -2003,6 +2016,15 @@ Object.assign(CodemanApp.prototype, { if (!filter && !sort) return; this._historyControlsWired = true; + if (sort) { + try { + const saved = localStorage.getItem(this._HISTORY_SORT_KEY); + if (saved && Array.from(sort.options).some((o) => o.value === saved)) sort.value = saved; + } catch { + /* private mode — the order just won't persist */ + } + } + if (filter) { filter.addEventListener('input', () => this._renderHistoryList()); filter.addEventListener('keydown', (ev) => { @@ -2014,7 +2036,16 @@ Object.assign(CodemanApp.prototype, { } }); } - if (sort) sort.addEventListener('change', () => this._renderHistoryList()); + if (sort) { + sort.addEventListener('change', () => { + try { + localStorage.setItem(this._HISTORY_SORT_KEY, sort.value); + } catch { + /* private mode — the order just won't persist */ + } + this._renderHistoryList(); + }); + } }, /** True when a past-session row matches the filter text (name, folder, case, prompt). */ @@ -2050,7 +2081,14 @@ Object.assign(CodemanApp.prototype, { const label = (s) => this._historyRowLabel(s, this._shortenHomePath(s.workingDir)).toLowerCase(); const folder = (s) => ((s.workingDir || '').split('/').pop() || '').toLowerCase(); const key = mode === 'name' ? label : folder; - const sorted = mode === 'recent' ? rows.slice() : rows.slice().sort((a, b) => key(a).localeCompare(key(b))); + // numeric collation so w2-… sorts before w10-…, and base sensitivity so case + // does not split a project's rows apart (from @jordan8037310's #263). + const sorted = + mode === 'recent' + ? rows.slice() + : rows + .slice() + .sort((a, b) => key(a).localeCompare(key(b), undefined, { sensitivity: 'base', numeric: true })); const pinned = sorted.filter((s) => s.pinned); return pinned.length === 0 ? sorted : pinned.concat(sorted.filter((s) => !s.pinned)); },