From d30ffcb50c8fe7ec23bc87b25677dcc183201ceb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 04:08:09 +0000 Subject: [PATCH 1/5] Fix thin reorder placeholder on desktop + polish bullet alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Section reorder placeholder no longer collapses to a thin strip on desktop. Sizing now comes from pill.offsetWidth/offsetHeight (transform- free, force layout) instead of getBoundingClientRect(), which could return intermediate values during the overlay's display:none → display:flex handoff while the entrance animation was still in its delay/FROM state. Width is also pinned on the placeholder so the slot can't collapse under flex reflow. CSS min-height: 44px + flex-shrink: 0 act as a final safety net for ≤90vh dialogs. - Bullet picker: Material Symbols icon bullets (37 styles) gained a top: 2px nudge in the shared rule so they sit on the text baseline in both .item-highlights and .custom-bullet-list, replacing the former .custom-bullet-list-only 1px nudge. small_square glyph now uses font-size: 11px; top: 1px to match the other small Unicode bullets. triangle, arrow, diamond, dash alignments unchanged. - Bullet picker dash glyph: em-dash (—) → en-dash (–) in both the rendered CSS and the picker preview — visibly shorter while keeping the horizontal-line shape. https://claude.ai/code/session_01JAjBc6BQUeR4vWV2RH3LRR --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- public/shared/admin.css | 5 +++++ public/shared/admin.js | 31 ++++++++++++++++++++----------- public/shared/styles.css | 11 +++++------ version.json | 2 +- 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffd1bd2c..69ca5fef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to CV Manager will be documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/), versioning follows [Semantic Versioning](https://semver.org/). +## [1.41.2] - 2026-04-20 + +### Fixed +- Section reorder overlay: the dashed placeholder that marks the dragged pill's slot no longer collapses to a thin strip on desktop. It was sized from `getBoundingClientRect().height`, which could return an intermediate value when the pill was measured while the overlay was still committing its `display:none → display:flex` handoff (the entrance animation is in its delay/FROM state, so computed layout hadn't settled). The drag now takes dimensions from `pill.offsetWidth` / `pill.offsetHeight` — both transform-free and force-layout — and applies width as well as height to the placeholder so the slot can't collapse if the flex container reflows. A CSS safety net (`min-height: 44px; flex-shrink: 0;`) also prevents the placeholder from being crushed on short viewports (≤ 90vh dialog cap). Mobile behavior is unchanged. +- Bullet picker alignment: Material Symbols icon bullets (37 styles) now sit on the text baseline instead of floating a couple pixels above it. The shared icon rule gained `top: 2px` to account for Material Symbols' built-in top padding, replacing the former `.custom-bullet-list`-only 1px nudge that didn't fix `.item-highlights`. The `small_square` glyph also now uses `font-size: 11px; top: 1px` to match the other small Unicode bullets (`bullet`, `hollow_circle`, `square`). `triangle`, `arrow`, `diamond`, and `dash` already sat correctly and are unchanged. +- Bullet picker `dash` glyph is now visibly shorter: the em-dash `—` (U+2014) was replaced with an en-dash `–` (U+2013) in both the rendered CSS and the picker preview so they match. + ## [1.41.1] - 2026-04-19 ### Fixed diff --git a/package-lock.json b/package-lock.json index 428fcc90..5ecee192 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cv-manager", - "version": "1.41.1", + "version": "1.41.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cv-manager", - "version": "1.41.1", + "version": "1.41.2", "dependencies": { "archiver": "^7.0.1", "better-sqlite3": "^9.4.3", diff --git a/package.json b/package.json index df93a2e5..7fb8eecd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cv-manager", - "version": "1.41.1", + "version": "1.41.2", "description": "Professional CV Management System", "main": "src/server.js", "scripts": { diff --git a/public/shared/admin.css b/public/shared/admin.css index 86f3fc9a..5975064a 100644 --- a/public/shared/admin.css +++ b/public/shared/admin.css @@ -3110,6 +3110,11 @@ body.reorder-active .section-reorder-handle { opacity: 0; pointer-events: none; border: 2px dashed rgba(255, 255, 255, 0.45); border-radius: var(--radius-lg); background: rgba(255, 255, 255, 0.08); + /* Safety net: if the inline height ever comes back as 0/intermediate, the + slot still renders at pill-height. Also prevent the flex column from + crushing the slot when the dialog hits its 90vh cap. */ + min-height: 44px; + flex-shrink: 0; } .reorder-footer { diff --git a/public/shared/admin.js b/public/shared/admin.js index 9104f507..414bdc78 100644 --- a/public/shared/admin.js +++ b/public/shared/admin.js @@ -3742,7 +3742,7 @@ const BULLET_STYLES = [ { id: 'small_square', glyph: '▪' }, { id: 'diamond', glyph: '◆' }, { id: 'arrow', glyph: '▸' }, - { id: 'dash', glyph: '—' }, + { id: 'dash', glyph: '–' }, { id: 'check', icon: 'check' }, { id: 'check_circle', icon: 'check_circle' }, { id: 'done_all', icon: 'done_all' }, @@ -5736,8 +5736,15 @@ function beginReorderDrag(key, pointerEvent, opts = {}) { if (reorderState.dragging) endReorderDrag(); const rect = pill.getBoundingClientRect(); - const pointerX = pointerEvent.clientX ?? (rect.left + rect.width / 2); - const pointerY = pointerEvent.clientY ?? (rect.top + rect.height / 2); + // offsetWidth / offsetHeight are transform-free and force layout, so they + // return the pill's stable border-box dimensions even while the entrance + // animation is in its delay / FROM state. rect.width / rect.height can be + // intermediate during the overlay's display:none → display:flex handoff + // on desktop, which collapses the placeholder to a thin strip. + const pillW = pill.offsetWidth || rect.width; + const pillH = pill.offsetHeight || rect.height; + const pointerX = pointerEvent.clientX ?? (rect.left + pillW / 2); + const pointerY = pointerEvent.clientY ?? (rect.top + pillH / 2); // If the pointer is outside the pill (e.g. drag initiated from the // section-heading handle on the left of the page), center the pill on @@ -5745,19 +5752,21 @@ function beginReorderDrag(key, pointerEvent, opts = {}) { // the pill floats far from the cursor and is hard to reach on a small // trackpad. Caller can also force this via opts.snapPillToCursor. const pointerOutsidePill = - pointerX < rect.left || pointerX > rect.right || - pointerY < rect.top || pointerY > rect.bottom; + pointerX < rect.left || pointerX > rect.left + pillW || + pointerY < rect.top || pointerY > rect.top + pillH; const snap = opts.snapPillToCursor || pointerOutsidePill; - // Placeholder keeps the slot while the pill floats + // Placeholder keeps the slot while the pill floats. Lock in both + // dimensions so the slot can't collapse if the flex container reflows. const placeholder = document.createElement('div'); placeholder.className = 'reorder-placeholder'; - placeholder.style.height = `${rect.height}px`; + placeholder.style.width = `${pillW}px`; + placeholder.style.height = `${pillH}px`; pill.parentNode.insertBefore(placeholder, pill); pill.classList.add('dragging'); - pill.style.width = `${rect.width}px`; - pill.style.height = `${rect.height}px`; + pill.style.width = `${pillW}px`; + pill.style.height = `${pillH}px`; pill.style.position = 'fixed'; pill.style.left = `${rect.left}px`; pill.style.top = `${rect.top}px`; @@ -5768,8 +5777,8 @@ function beginReorderDrag(key, pointerEvent, opts = {}) { key, pill, placeholder, - offsetX: snap ? rect.width / 2 : pointerX - rect.left, - offsetY: snap ? rect.height / 2 : pointerY - rect.top, + offsetX: snap ? pillW / 2 : pointerX - rect.left, + offsetY: snap ? pillH / 2 : pointerY - rect.top, pointerId: pointerEvent.pointerId }; diff --git a/public/shared/styles.css b/public/shared/styles.css index 079b0bbd..bb19dc31 100644 --- a/public/shared/styles.css +++ b/public/shared/styles.css @@ -1105,13 +1105,13 @@ body[data-bullet-style="hollow_circle"] .custom-bullet-list li::before { content body[data-bullet-style="square"] .item-highlights li::before, body[data-bullet-style="square"] .custom-bullet-list li::before { content: '■'; font-size: 9px; top: 2px; } body[data-bullet-style="small_square"] .item-highlights li::before, -body[data-bullet-style="small_square"] .custom-bullet-list li::before { content: '▪'; } +body[data-bullet-style="small_square"] .custom-bullet-list li::before { content: '▪'; font-size: 11px; top: 1px; } body[data-bullet-style="diamond"] .item-highlights li::before, body[data-bullet-style="diamond"] .custom-bullet-list li::before { content: '◆'; } body[data-bullet-style="arrow"] .item-highlights li::before, body[data-bullet-style="arrow"] .custom-bullet-list li::before { content: '▸'; } body[data-bullet-style="dash"] .item-highlights li::before, -body[data-bullet-style="dash"] .custom-bullet-list li::before { content: '—'; font-weight: normal; } +body[data-bullet-style="dash"] .custom-bullet-list li::before { content: '–'; font-weight: normal; } /* Material Symbols icon styles — one shared rule targets every icon bullet via `data-bullet-kind="icon"`, which admin.js / public-readonly set based @@ -1123,6 +1123,9 @@ body[data-bullet-kind="icon"] .custom-bullet-list li::before { font-weight: 400; font-size: 14px; line-height: 1.2; + /* Material Symbols have more top padding than the triangle baseline; + nudge down so the glyph sits on the text baseline in both contexts. */ + top: 2px; } body[data-bullet-style="check"] .item-highlights li::before, body[data-bullet-style="check"] .custom-bullet-list li::before { content: 'check'; } @@ -1219,10 +1222,6 @@ body[data-bullet-style="local_fire_department"] .custom-bullet-list li::before { body[data-bullet-style="edit_note"] .item-highlights li::before, body[data-bullet-style="edit_note"] .custom-bullet-list li::before { content: 'edit_note'; } -/* Custom bullet-list has more line-height than item-highlights; nudge the - icon down 1px so it aligns with the first text baseline. */ -body[data-bullet-kind="icon"] .custom-bullet-list li::before { top: 1px; } - /* Free Text Layout */ .custom-free-text-blocks { display: flex; diff --git a/version.json b/version.json index 12cc960b..359cf1dd 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "version": "1.41.1", + "version": "1.41.2", "changelog": "https://github.com/vincentmakes/cv-manager/blob/main/CHANGELOG.md" } From 18d172362568bb320e63131d39e9f5227f6b3a19 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 04:19:39 +0000 Subject: [PATCH 2/5] =?UTF-8?q?Bullet=20icon=20nudge:=202px=20=E2=86=92=20?= =?UTF-8?q?1px=20to=20align=20with=20triangle/arrow/diamond?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2px top-offset on Material Symbols icon bullets (introduced in d30ffcb) pushed icons below the triangle/arrow/diamond baseline, so those three glyphs — whose own rules were untouched — looked "too high" relative to the icons. Reducing the nudge to 1px (matching the former custom-bullet-list-only value that was already tuned to the triangle line) lines icons up with the glyph baseline without shifting the triangle/arrow/diamond position. https://claude.ai/code/session_01JAjBc6BQUeR4vWV2RH3LRR --- public/shared/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/shared/styles.css b/public/shared/styles.css index bb19dc31..a296167b 100644 --- a/public/shared/styles.css +++ b/public/shared/styles.css @@ -1124,8 +1124,8 @@ body[data-bullet-kind="icon"] .custom-bullet-list li::before { font-size: 14px; line-height: 1.2; /* Material Symbols have more top padding than the triangle baseline; - nudge down so the glyph sits on the text baseline in both contexts. */ - top: 2px; + nudge down so the glyph aligns with the triangle/arrow/diamond line. */ + top: 1px; } body[data-bullet-style="check"] .item-highlights li::before, body[data-bullet-style="check"] .custom-bullet-list li::before { content: 'check'; } From fbeae2a51c2433bdb0bf3921e6d41abc17b3b14f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 04:28:08 +0000 Subject: [PATCH 3/5] =?UTF-8?q?Revert=20"Bullet=20icon=20nudge:=202px=20?= =?UTF-8?q?=E2=86=92=201px=20to=20align=20with=20triangle/arrow/diamond"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 18d172362568bb320e63131d39e9f5227f6b3a19. --- public/shared/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/shared/styles.css b/public/shared/styles.css index a296167b..bb19dc31 100644 --- a/public/shared/styles.css +++ b/public/shared/styles.css @@ -1124,8 +1124,8 @@ body[data-bullet-kind="icon"] .custom-bullet-list li::before { font-size: 14px; line-height: 1.2; /* Material Symbols have more top padding than the triangle baseline; - nudge down so the glyph aligns with the triangle/arrow/diamond line. */ - top: 1px; + nudge down so the glyph sits on the text baseline in both contexts. */ + top: 2px; } body[data-bullet-style="check"] .item-highlights li::before, body[data-bullet-style="check"] .custom-bullet-list li::before { content: 'check'; } From 1b75246f8752b708517ad8f9b85b52038e947785 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 04:32:04 +0000 Subject: [PATCH 4/5] Nudge square bullet down to sit on the text baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The filled ■ at font-size 9px with top: 2px still floated above the 13-px text line — visually "too high". Bumping top to 5px centers the small square on the text baseline in both .item-highlights and .custom-bullet-list. https://claude.ai/code/session_01JAjBc6BQUeR4vWV2RH3LRR --- public/shared/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/shared/styles.css b/public/shared/styles.css index bb19dc31..b066b375 100644 --- a/public/shared/styles.css +++ b/public/shared/styles.css @@ -1103,7 +1103,7 @@ body[data-bullet-style="bullet"] .custom-bullet-list li::before { content: '●' body[data-bullet-style="hollow_circle"] .item-highlights li::before, body[data-bullet-style="hollow_circle"] .custom-bullet-list li::before { content: '○'; font-size: 10px; top: 1px; } body[data-bullet-style="square"] .item-highlights li::before, -body[data-bullet-style="square"] .custom-bullet-list li::before { content: '■'; font-size: 9px; top: 2px; } +body[data-bullet-style="square"] .custom-bullet-list li::before { content: '■'; font-size: 9px; top: 5px; } body[data-bullet-style="small_square"] .item-highlights li::before, body[data-bullet-style="small_square"] .custom-bullet-list li::before { content: '▪'; font-size: 11px; top: 1px; } body[data-bullet-style="diamond"] .item-highlights li::before, From 701e1f98b101a5f9edb15b4eacb0eb27a8e4847e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 04:40:48 +0000 Subject: [PATCH 5/5] Vertically center bullet glyphs via flex instead of per-style top nudges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each per-style `top: Npx` offset was a hand-tuned guess that never quite worked for every glyph at once — making one bullet look centered inevitably left another bullet a pixel or two off. Both baseline ::before rules now declare a fixed-height flex container (20.8 px = 13 px × 1.6 line-height of the li) and center the glyph with `align-items: center`. Any glyph size (9 px filled circle, 13 px triangle, 14 px Material Symbols icon) drops into the same centered slot, so every bullet style lines up on the first text line without a per-style nudge. Per-style rules keep their `font-size` overrides (for visual glyph size) and lose their `top` offsets. The icon rule loses its 2 px nudge and drops `line-height: 1.2` → `line-height: 1` since the outer flex box now owns vertical alignment. A print-media override sets the bullet box to 17.6 px (11 px × 1.6) to match the smaller print line height. https://claude.ai/code/session_01JAjBc6BQUeR4vWV2RH3LRR --- public/shared/styles.css | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/public/shared/styles.css b/public/shared/styles.css index b066b375..de971fa4 100644 --- a/public/shared/styles.css +++ b/public/shared/styles.css @@ -593,6 +593,13 @@ body { content: '▹'; position: absolute; left: 0; + top: 0; + /* Fixed-height flex box centers whatever glyph/icon fills it on the + first text line of the li, independent of the glyph's own font-size. + 13px × 1.6 line-height = 20.8px (matches the li's first-line height). */ + height: 20.8px; + display: inline-flex; + align-items: center; color: var(--primary); font-weight: bold; } @@ -1083,6 +1090,11 @@ body { content: '▹'; position: absolute; left: 0; + top: 0; + /* Same flex-centering trick as .item-highlights — see the comment there. */ + height: 20.8px; + display: inline-flex; + align-items: center; color: var(--primary); font-weight: bold; } @@ -1097,15 +1109,17 @@ body { locations (experience highlights + custom bullet-list sections) so a single setting applies consistently across the CV. */ -/* Unicode-glyph styles */ +/* Unicode-glyph styles — font-size controls the glyph's visual size; the + flex-centering on the baseline ::before rule handles vertical alignment, + so no per-style `top` nudges are needed. */ body[data-bullet-style="bullet"] .item-highlights li::before, -body[data-bullet-style="bullet"] .custom-bullet-list li::before { content: '●'; font-size: 9px; top: 2px; } +body[data-bullet-style="bullet"] .custom-bullet-list li::before { content: '●'; font-size: 9px; } body[data-bullet-style="hollow_circle"] .item-highlights li::before, -body[data-bullet-style="hollow_circle"] .custom-bullet-list li::before { content: '○'; font-size: 10px; top: 1px; } +body[data-bullet-style="hollow_circle"] .custom-bullet-list li::before { content: '○'; font-size: 10px; } body[data-bullet-style="square"] .item-highlights li::before, -body[data-bullet-style="square"] .custom-bullet-list li::before { content: '■'; font-size: 9px; top: 5px; } +body[data-bullet-style="square"] .custom-bullet-list li::before { content: '■'; font-size: 9px; } body[data-bullet-style="small_square"] .item-highlights li::before, -body[data-bullet-style="small_square"] .custom-bullet-list li::before { content: '▪'; font-size: 11px; top: 1px; } +body[data-bullet-style="small_square"] .custom-bullet-list li::before { content: '▪'; font-size: 11px; } body[data-bullet-style="diamond"] .item-highlights li::before, body[data-bullet-style="diamond"] .custom-bullet-list li::before { content: '◆'; } body[data-bullet-style="arrow"] .item-highlights li::before, @@ -1122,10 +1136,7 @@ body[data-bullet-kind="icon"] .custom-bullet-list li::before { font-family: 'Material Symbols Outlined'; font-weight: 400; font-size: 14px; - line-height: 1.2; - /* Material Symbols have more top padding than the triangle baseline; - nudge down so the glyph sits on the text baseline in both contexts. */ - top: 2px; + line-height: 1; } body[data-bullet-style="check"] .item-highlights li::before, body[data-bullet-style="check"] .custom-bullet-list li::before { content: 'check'; } @@ -1922,6 +1933,9 @@ body.has-preview-banner .container { .item-location { font-size: 11px; } .item-summary { font-size: 11px; } .item-highlights li { font-size: 11px; margin-bottom: 2px; } + /* 11px × 1.6 line-height — keep bullets vertically centered on print. */ + .item-highlights li::before, + .custom-bullet-list li::before { height: 17.6px; } /* Certs - force grid layout like desktop */ .cert-grid {