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) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-08-10 03:12:02 +02:00
parent 5d42f64393
commit 053a6d238d
+43 -5
View File
@@ -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));
},