mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix(run-menu): make Recent Sessions rows legible on macOS
Closes #273. Every row in the Run dropdown's Recent Sessions list rendered as `/Users/<user>/co…`, indistinguishable from every other row. The width was the symptom. The cause is that the home-prefix abbreviation matched `/home/<user>/` only: s.workingDir.replace(/^\/home\/[^/]+\//, '~/') On macOS the prefix is `/Users/<user>/`, so nothing was stripped and every row spent its first ~19 characters on an identical prefix, with left-to-right ellipsis cutting the only part that identifies it. The 250px menu cap was chosen, per its own comment, as "the width at which the common `~/<dir>/<repo>` + timestamp recent-session row still fits whole" — sizing that assumes the abbreviation ran. On Linux it does. On macOS the menu was permanently too narrow for content it was never actually shortening, which is why this reads as fine on one platform and broken on the other. Changes: - the regex matches `/home/` and `/Users/` - the row leads with the identifying folder in semibold, with the parent path trailing, dimmed and right-aligned, so truncation removes context instead of identity - the menu goes full width above 769px and the history list grows 200px -> 320px. Phones keep the compact popover deliberately: mobile.css positions this menu itself and a viewport-wide drawer there would cover the composer - a worktree pill renders from the fields /api/history/sessions already returns unprojected (#266/#269), since a worktree's directory basename is often just the worktree name and rows stayed ambiguous without it - a trailing `/.claude/worktrees` is trimmed from the displayed parent path once the pill states it, so the repo name stays visible Verified in a browser at 1440px against a real 38-session history: menu 1416px, 0 of 34 rows clip their project name (was: all of them), 9 worktree pills render, no page errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016uTqt8ttmsBLXbm5JFHis3
This commit is contained in:
co-authored by
Claude Opus 5
parent
752374abc7
commit
3a106bd048
@@ -489,23 +489,55 @@ Object.assign(CodemanApp.prototype, {
|
||||
const date = new Date(s.lastModified);
|
||||
const timeStr = date.toLocaleDateString('en', { month: 'short', day: 'numeric' })
|
||||
+ ' ' + date.toLocaleTimeString('en', { hour: '2-digit', minute: '2-digit', hour12: false });
|
||||
const shortDir = s.workingDir.replace(/^\/home\/[^/]+\//, '~/');
|
||||
// Abbreviate the home prefix. This used to match `/home/<user>/` only, so
|
||||
// on macOS (`/Users/<user>/`) nothing was stripped and every row spent its
|
||||
// first ~19 characters on an identical prefix — with the tail ellipsized,
|
||||
// all rows rendered as `/Users/jordanryan/co…` and became indistinguishable.
|
||||
const shortDir = s.workingDir.replace(/^\/(?:home|Users)\/[^/]+\//, '~/');
|
||||
// Lead with the folder that identifies the row; the parent path trails and
|
||||
// is what gets truncated. Truncation must never eat the identity.
|
||||
const lastSlash = shortDir.lastIndexOf('/');
|
||||
const leafName = lastSlash === -1 ? shortDir : shortDir.slice(lastSlash + 1);
|
||||
// `<repo>/.claude/worktrees` in the parent path is pure noise once the pill
|
||||
// says which worktree it is — drop it so the repo stays visible instead.
|
||||
const parentDir = (lastSlash === -1 ? '' : shortDir.slice(0, lastSlash)).replace(/\/\.claude\/worktrees$/, '');
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'run-mode-option';
|
||||
btn.className = 'run-mode-option run-mode-hist-row';
|
||||
btn.title = s.workingDir;
|
||||
btn.dataset.sessionId = s.sessionId;
|
||||
btn.dataset.workingDir = s.workingDir;
|
||||
|
||||
const dirSpan = document.createElement('span');
|
||||
dirSpan.className = 'hist-dir';
|
||||
dirSpan.textContent = shortDir;
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'hist-name';
|
||||
nameSpan.textContent = leafName;
|
||||
|
||||
const parts = [nameSpan];
|
||||
|
||||
// Worktree pill, same data the session rows use (#266). A worktree's
|
||||
// directory basename is often just the worktree name, so without this two
|
||||
// worktrees of one repo still read alike.
|
||||
const wt = this._worktreeLabel ? this._worktreeLabel(s) : '';
|
||||
if (wt) {
|
||||
const wtSpan = document.createElement('span');
|
||||
wtSpan.className = 'hist-wt';
|
||||
wtSpan.textContent = wt;
|
||||
parts.push(wtSpan);
|
||||
}
|
||||
|
||||
if (parentDir) {
|
||||
const dirSpan = document.createElement('span');
|
||||
dirSpan.className = 'hist-dir';
|
||||
dirSpan.textContent = parentDir;
|
||||
parts.push(dirSpan);
|
||||
}
|
||||
|
||||
const metaSpan = document.createElement('span');
|
||||
metaSpan.className = 'hist-meta';
|
||||
metaSpan.textContent = timeStr;
|
||||
parts.push(metaSpan);
|
||||
|
||||
btn.append(dirSpan, metaSpan);
|
||||
btn.append(...parts);
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.resumeHistorySession(s.sessionId, s.workingDir, s.name);
|
||||
|
||||
@@ -4392,6 +4392,24 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
max-width: 250px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Above phone width the menu becomes a full-width drawer across the bottom of the
|
||||
window. The 250px cap above was sized for a `~/<dir>/<repo>` row, which assumed
|
||||
the home prefix had been abbreviated — it never was on macOS, so real rows blew
|
||||
straight through it and ellipsized into `/Users/<user>/co…`, identical on every
|
||||
line. Width is what buys room for the worktree + branch, so the cap is lifted
|
||||
rather than nudged. Phones keep the compact popover: mobile.css positions this
|
||||
menu itself and a viewport-wide drawer there would cover the composer. */
|
||||
@media (min-width: 769px) {
|
||||
.run-mode-menu {
|
||||
max-width: none;
|
||||
width: calc(100vw - 24px);
|
||||
}
|
||||
.run-mode-history {
|
||||
/* More rows are worth showing once each one is legible. */
|
||||
max-height: 320px;
|
||||
}
|
||||
}
|
||||
.run-mode-menu.active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -4462,6 +4480,31 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
.run-mode-history .run-mode-option {
|
||||
max-width: 100%;
|
||||
}
|
||||
/* Recent-session row: identity first, path last.
|
||||
The row reads <folder> [⑂ worktree · branch] <parent path> <time>
|
||||
with only the parent path allowed to shrink, so truncation can never hide
|
||||
which project (or which worktree) a row refers to. */
|
||||
.run-mode-hist-row {
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
.run-mode-option .hist-name {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
}
|
||||
.run-mode-option .hist-wt {
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.78em;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
||||
color: var(--accent);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.run-mode-option .hist-dir {
|
||||
flex: 1;
|
||||
/* A flex item's default min-width is auto, so without this the path refuses to
|
||||
@@ -4471,6 +4514,11 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
/* The parent path is context, not identity — dim it and let it be the part
|
||||
that loses characters first. */
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85em;
|
||||
text-align: right;
|
||||
}
|
||||
.run-mode-option .hist-meta {
|
||||
font-size: 0.85em;
|
||||
|
||||
Reference in New Issue
Block a user