Merge git-panel-layout-fixes: right-pinned worktree actions, count on the heading
This commit is contained in:
@@ -100,8 +100,16 @@ function renderCommitRow(c: CommitLogEntry): HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Render a GitLogResult into a container (clears first). Empty → an inert note. */
|
/** Render a GitLogResult into a container (clears first). Empty → an inert note. */
|
||||||
export function renderGitLog(container: HTMLElement, log: GitLogResult): void {
|
export function renderGitLog(
|
||||||
|
container: HTMLElement,
|
||||||
|
log: GitLogResult,
|
||||||
|
labelEl?: HTMLElement,
|
||||||
|
): void {
|
||||||
container.textContent = ''
|
container.textContent = ''
|
||||||
|
// Design: the count sits ON the section heading, not on a line of its own —
|
||||||
|
// it qualifies "Recent commits", it is not a separate statement.
|
||||||
|
const badgeHost = labelEl ?? container
|
||||||
|
badgeHost.querySelector('.proj-commitlog-count')?.remove()
|
||||||
if (log.commits.length === 0) {
|
if (log.commits.length === 0) {
|
||||||
container.append(el('div', 'proj-empty', 'No commits yet.'))
|
container.append(el('div', 'proj-empty', 'No commits yet.'))
|
||||||
return
|
return
|
||||||
@@ -110,9 +118,9 @@ export function renderGitLog(container: HTMLElement, log: GitLogResult): void {
|
|||||||
// readable before the eye has walked the rows looking for rails.
|
// readable before the eye has walked the rows looking for rails.
|
||||||
const unpushedCount = log.commits.filter((c) => c.unpushed === true).length
|
const unpushedCount = log.commits.filter((c) => c.unpushed === true).length
|
||||||
if (unpushedCount > 0 && log.upstream !== undefined) {
|
if (unpushedCount > 0 && log.upstream !== undefined) {
|
||||||
const badge = el('div', 'proj-commitlog-count', `↑ ${unpushedCount} unpushed`)
|
const badge = el('span', 'proj-commitlog-count', `↑ ${unpushedCount} unpushed`)
|
||||||
badge.title = `${unpushedCount} commit(s) not on ${log.upstream}`
|
badge.title = `${unpushedCount} commit(s) not on ${log.upstream}`
|
||||||
container.append(badge)
|
badgeHost.append(badge)
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = el('div', 'proj-commitlog-list')
|
const list = el('div', 'proj-commitlog-list')
|
||||||
@@ -150,7 +158,11 @@ export interface GitLogHandle {
|
|||||||
* the log, then swap in the rows. A fetch failure degrades to a short inert
|
* the log, then swap in the rows. A fetch failure degrades to a short inert
|
||||||
* message (never throws). destroy() removes the node and cancels the swap.
|
* message (never throws). destroy() removes the node and cancels the swap.
|
||||||
*/
|
*/
|
||||||
export function mountGitLog(container: HTMLElement, repoPath: string): GitLogHandle {
|
export function mountGitLog(
|
||||||
|
container: HTMLElement,
|
||||||
|
repoPath: string,
|
||||||
|
labelEl?: HTMLElement,
|
||||||
|
): GitLogHandle {
|
||||||
let destroyed = false
|
let destroyed = false
|
||||||
|
|
||||||
container.textContent = ''
|
container.textContent = ''
|
||||||
@@ -164,7 +176,7 @@ export function mountGitLog(container: HTMLElement, repoPath: string): GitLogHan
|
|||||||
container.append(el('div', 'proj-empty', 'Could not read recent commits.'))
|
container.append(el('div', 'proj-empty', 'Could not read recent commits.'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
renderGitLog(container, log)
|
renderGitLog(container, log, labelEl)
|
||||||
})()
|
})()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Binary file not shown.
@@ -1725,18 +1725,17 @@ body {
|
|||||||
.proj-commitlog-count {
|
.proj-commitlog-count {
|
||||||
font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
||||||
font-size: 11.5px;
|
font-size: 11.5px;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
margin: -4px 0 6px;
|
text-transform: none;
|
||||||
|
letter-spacing: 0;
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── w6/G5: worktree rows as two-line cards ──────────────────────────────── */
|
/* ── w6/G5: worktree rows as two-line cards ──────────────────────────────── */
|
||||||
.proj-wt-row {
|
/* NOTE: the row's own display/grid lives with the ORIGINAL .proj-wt-row rule
|
||||||
display: grid;
|
further down. Declaring it twice let the later copy silently win and the
|
||||||
grid-template-columns: 1fr auto;
|
Open button never reached the right edge. One rule per selector. */
|
||||||
gap: 14px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proj-wt-row-current {
|
.proj-wt-row-current {
|
||||||
border-color: rgba(227, 166, 74, 0.36);
|
border-color: rgba(227, 166, 74, 0.36);
|
||||||
background: linear-gradient(90deg, var(--accent-soft), transparent 46%);
|
background: linear-gradient(90deg, var(--accent-soft), transparent 46%);
|
||||||
@@ -2349,10 +2348,12 @@ body {
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
.proj-wt-row {
|
.proj-wt-row {
|
||||||
display: flex;
|
/* w6: two-line card — content column, then the actions rail pinned right. */
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 14px;
|
||||||
padding: 9px 12px;
|
padding: 11px 14px;
|
||||||
background: var(--surface-2);
|
background: var(--surface-2);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@@ -217,14 +217,44 @@ describe('renderGitLog — unpushed count beside the heading (w6 design)', () =>
|
|||||||
return { hash, at: Date.now(), subject, ...(unpushed === true ? { unpushed: true } : {}) }
|
return { hash, at: Date.now(), subject, ...(unpushed === true ? { unpushed: true } : {}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
it('states the count so it is readable before scanning the rows', () => {
|
it('puts the count ON the heading when one is supplied, not on its own line', () => {
|
||||||
const host = document.createElement('div')
|
const host = document.createElement('div')
|
||||||
renderGitLog(host, {
|
const label = document.createElement('div')
|
||||||
|
label.textContent = 'Recent commits'
|
||||||
|
renderGitLog(
|
||||||
|
host,
|
||||||
|
{
|
||||||
commits: [entry('a1', 'x', true), entry('b2', 'y', true), entry('c3', 'z')],
|
commits: [entry('a1', 'x', true), entry('b2', 'y', true), entry('c3', 'z')],
|
||||||
truncated: false,
|
truncated: false,
|
||||||
upstream: 'origin/develop',
|
upstream: 'origin/develop',
|
||||||
|
},
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
expect(label.querySelector('.proj-commitlog-count')!.textContent).toContain('2')
|
||||||
|
expect(host.querySelector('.proj-commitlog-count')).toBeNull()
|
||||||
})
|
})
|
||||||
expect(host.querySelector('.proj-commitlog-count')!.textContent).toContain('2')
|
|
||||||
|
it('does not stack duplicate counts across re-renders', () => {
|
||||||
|
const host = document.createElement('div')
|
||||||
|
const label = document.createElement('div')
|
||||||
|
const log = {
|
||||||
|
commits: [entry('a1', 'x', true)],
|
||||||
|
truncated: false,
|
||||||
|
upstream: 'origin/develop',
|
||||||
|
}
|
||||||
|
renderGitLog(host, log, label)
|
||||||
|
renderGitLog(host, log, label)
|
||||||
|
expect(label.querySelectorAll('.proj-commitlog-count').length).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to the list container when no heading is given', () => {
|
||||||
|
const host = document.createElement('div')
|
||||||
|
renderGitLog(host, {
|
||||||
|
commits: [entry('a1', 'x', true)],
|
||||||
|
truncated: false,
|
||||||
|
upstream: 'origin/develop',
|
||||||
|
})
|
||||||
|
expect(host.querySelector('.proj-commitlog-count')).not.toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('omits the count when nothing is unpushed', () => {
|
it('omits the count when nothing is unpushed', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user