Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
5 changes: 5 additions & 0 deletions public/shared/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
31 changes: 20 additions & 11 deletions public/shared/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand Down Expand Up @@ -5736,28 +5736,37 @@ 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
// the cursor instead of preserving the pointer→pill offset. Otherwise
// 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`;
Expand All @@ -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
};

Expand Down
35 changes: 24 additions & 11 deletions public/shared/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -1097,21 +1109,23 @@ 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: 2px; }
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: '▪'; }
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,
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
Expand All @@ -1122,7 +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;
line-height: 1;
}
body[data-bullet-style="check"] .item-highlights li::before,
body[data-bullet-style="check"] .custom-bullet-list li::before { content: 'check'; }
Expand Down Expand Up @@ -1219,10 +1233,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;
Expand Down Expand Up @@ -1923,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 {
Expand Down
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "1.41.1",
"version": "1.41.2",
"changelog": "https://github.com/vincentmakes/cv-manager/blob/main/CHANGELOG.md"
}
Loading