|
26 | 26 | .sesscard { position: relative; padding: 7px 8px 8px; margin-bottom: 1px; border-radius: 6px; cursor: pointer; transition: background .1s; } |
27 | 27 | .sesscard:hover { background: color-mix(in srgb, var(--cc-text3) 13%, transparent); } |
28 | 28 | .sesscard:focus-visible { background: color-mix(in srgb, var(--cc-accent) 15%, transparent); outline: none; } |
29 | | - .sesscard .sesstop { display: flex; align-items: center; gap: 10px; min-height: 20px; } |
| 29 | + .sesscard .sesstop { display: flex; align-items: center; gap: 10px; } |
30 | 30 | .sesscard .sesstop::before { content: ''; flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; background: var(--cc-text3); } |
31 | 31 | .sesscard.warn .sesstop::before { background: var(--cc-warn); } |
32 | 32 | .sesscard.err .sesstop::before { background: var(--cc-danger); } |
33 | 33 | .sesscard.res .sesstop::before { background: var(--cc-accent); } |
34 | 34 | .sesscard .sessname { font-weight: 400; font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; } |
35 | | - .sesscard .sesssub { margin: 2px 0 0 16px; font-size: 12px; color: var(--cc-text3); } |
36 | | - /* Row actions: compact icon buttons on the right, revealed on hover — no drawer, so the row never reflows |
37 | | - or floats over its neighbours. sesstop reserves the icon height (min-height) so the appearing icons |
38 | | - don't nudge the line. The pin stays visible at rest when pinned. */ |
39 | | - .sesscard .sessacts { display: none; align-items: center; gap: 1px; flex: 0 0 auto; } |
| 35 | + /* Line 2 shares its slot: at rest the file·time line; on hover the labeled buttons take the same |
| 36 | + fixed-height row (min-height) at full width — nothing wraps, reflows, or floats, and the title above is |
| 37 | + left intact. Sidebar buttons are label-only (icon hidden) to stay compact in a narrow panel. */ |
| 38 | + .sesscard .sessline2 { margin: 2px 0 0 16px; min-height: 22px; display: flex; align-items: center; } |
| 39 | + .sesscard .sesssub { font-size: 12px; color: var(--cc-text3); } |
| 40 | + .sesscard .sessacts { display: none; align-items: center; gap: 4px; flex-wrap: nowrap; } |
| 41 | + .sesscard:hover .sesssub, .sesscard:focus-within .sesssub { display: none; } |
40 | 42 | .sesscard:hover .sessacts, .sesscard:focus-within .sessacts { display: flex; } |
41 | | - .sesscard .sessact { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 20px; padding: 0; border: none; background: transparent; color: var(--cc-text3); border-radius: 4px; cursor: pointer; } |
42 | | - .sesscard .sessact:hover { background: color-mix(in srgb, var(--cc-text3) 22%, transparent); color: var(--cc-text); } |
43 | | - .sesscard .sessact.sesspin { display: none; } |
44 | | - .sesscard.pinned .sesspin, .sesscard:hover .sesspin, .sesscard:focus-within .sesspin { display: inline-flex; } |
45 | | - .sesscard.pinned .sesspin { color: var(--cc-accent); } |
46 | | - .sesscard .ci { width: 13px; height: 13px; } |
| 43 | + .sesscard .sessact { display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border: none; background: transparent; color: var(--cc-text2); border-radius: 4px; cursor: pointer; font: inherit; font-size: 11px; white-space: nowrap; } |
| 44 | + .sesscard .sessact .ci { display: none; } |
| 45 | + .sesscard .sessact:hover { background: color-mix(in srgb, var(--cc-text3) 20%, transparent); color: var(--cc-text); } |
| 46 | + .sesscard .sessact[data-act="delete"]:hover { background: color-mix(in srgb, var(--cc-danger) 18%, transparent); color: var(--cc-danger); } |
| 47 | + .sesscard.pinned [data-act="pin"] { color: var(--cc-accent); } |
47 | 48 | @media (prefers-reduced-motion: reduce) { .sesscard { transition: none; } } |
48 | 49 | </style> |
49 | 50 | </head> |
|
86 | 87 | star: '<svg class="ci" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 1.5 L9.53 5.9 L14.18 5.99 L10.47 8.8 L11.82 13.26 L8 10.6 L4.18 13.26 L5.53 8.8 L1.82 5.99 L6.47 5.9 Z"/></svg>' |
87 | 88 | }; |
88 | 89 | function sessActBtn(act, label, svg, cls){ |
89 | | - return '<button type="button" class="sessact' + (cls ? ' ' + cls : '') + '" data-act="' + act + '" title="' + label + '" aria-label="' + label + '">' + svg + '</button>'; |
| 90 | + return '<button type="button" class="sessact' + (cls ? ' ' + cls : '') + '" data-act="' + act + '" title="' + label + '" aria-label="' + label + '">' + svg + '<span class="sesslbl">' + label + '</span></button>'; |
90 | 91 | } |
91 | 92 | var SESS_BUCKETS = ['Pinned', 'Today', 'Yesterday', 'This week', 'Earlier']; |
92 | 93 | function sessGroup(entries, nowMs){ |
|
95 | 96 | return SESS_BUCKETS.filter((b) => by[b].length).map((b) => ({ bucket: b, entries: by[b] })); |
96 | 97 | } |
97 | 98 | // esc/escAttr are INJECTED so this whole block stays pure (unit-testable without the webview globals). |
98 | | - // The card is two fixed-height lines: title + (primary file · time). Actions are compact icon buttons on |
99 | | - // the RIGHT of the title row — hidden at rest, revealed on hover — so nothing reflows or floats. Clicking |
100 | | - // the card body resumes (so there is no Resume icon); the pin stays visible at rest when pinned. Secondary |
101 | | - // detail (model · turns · files) rides the native tooltip. |
| 99 | + // Two fixed-height lines: the title, then (primary file · time). On hover the SECOND line swaps to the |
| 100 | + // labeled action buttons — Rename · Done · Delete · Pin — in the same fixed-height row, at full width, so |
| 101 | + // nothing wraps, reflows, or floats and the title is left intact. Clicking the card body resumes (no |
| 102 | + // Resume button). Pinned is shown by the Pinned bucket + the accent Unpin button; secondary detail |
| 103 | + // (model · turns · files) rides the native tooltip. |
102 | 104 | function sessCardHtml(e, nowMs, escFn, escAttrFn){ |
103 | 105 | const st = e.state === 'interrupted' ? ' warn' : e.state === 'error' ? ' err' : e.state === 'resumed' ? ' res' : ''; |
104 | 106 | const files = e.filesEdited || []; |
|
108 | 110 | + (e.model ? ' · ' + sessModelShort(e.model) : '') + (files.length ? ' · ' + files.join(', ') : ''); |
109 | 111 | const acts = '<span class="sessacts">' |
110 | 112 | + sessActBtn('rename', 'Rename', SESS_IC.edit) |
111 | | - + sessActBtn('done', 'Archive', SESS_IC.check) |
| 113 | + + sessActBtn('done', 'Done', SESS_IC.check) |
112 | 114 | + sessActBtn('delete', 'Delete', SESS_IC.trash) |
113 | | - + '</span>' |
114 | | - + sessActBtn('pin', e.pinned ? 'Unpin' : 'Pin', SESS_IC.star, 'sesspin'); |
| 115 | + + sessActBtn('pin', e.pinned ? 'Unpin' : 'Pin', SESS_IC.star) |
| 116 | + + '</span>'; |
115 | 117 | return '<div class="sesscard' + st + (e.pinned ? ' pinned' : '') + '" data-id="' + escAttrFn(e.id || '') + '" tabindex="0" role="button" aria-label="' + escAttrFn(e.title || 'Untitled') + '" title="' + escAttrFn(detail) + '">' |
116 | | - + '<div class="sesstop"><span class="sessname">' + escFn(e.title || 'Untitled') + '</span>' + acts + '</div>' |
117 | | - + '<div class="sesssub">' + sub + '</div></div>'; |
| 118 | + + '<div class="sesstop"><span class="sessname">' + escFn(e.title || 'Untitled') + '</span></div>' |
| 119 | + + '<div class="sessline2"><span class="sesssub">' + sub + '</span>' + acts + '</div></div>'; |
118 | 120 | } |
119 | 121 | // [SESSIONS-PURE-END] |
120 | 122 | const D = Date.now(); |
|
0 commit comments