Many UX and correctness improvements

- Components URL reflects selected component (/components/:id), survives refresh
- Word-split search: "0603 res" matches "Resistor 0603"
- Left pane resizable with localStorage persistence
- Field values rendered via central render_field_value() (units, URLs, extensible)
- Fields sorted alphabetically in both detail view and edit dialog
- Edit component dialog widened; field rows use shared grid columns (table-like)
- No space between value and unit (supports prefix suffixes like k, M, µ)
- Grid viewer highlights and scrolls to cell when navigating from component detail
- Cell inventory overlay items are <a> tags — middle-click opens in new tab
- PDF files stored with sanitized human-readable filename, not random ID
- PDF rename also renames file on disk
- Atomic rename via renameat2(RENAME_NOREPLACE) through tools/mv-sync
- Fix .cell-thumb-link → .cell-thumb-preview (div, not anchor), cursor: zoom-in
- Fix field name overflow in detail view (auto column width, overflow-wrap)
- Fix link color: use --accent instead of browser default dark blue

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 02:49:11 +00:00
parent 7ef5bb5381
commit 58c93f2bd0
7 changed files with 233 additions and 51 deletions

View File

@@ -32,6 +32,14 @@
--font-mono: 'Fira Code', 'Cascadia Code', monospace;
}
a {
color: var(--accent);
}
a:hover {
color: var(--accent-hover);
}
body {
background: var(--bg);
color: var(--text);
@@ -185,10 +193,23 @@ nav {
.split-layout {
display: flex;
gap: 1.25rem;
align-items: flex-start;
}
.split-resizer {
width: 5px;
flex-shrink: 0;
align-self: stretch;
cursor: col-resize;
background: transparent;
transition: background 0.15s;
}
.split-resizer:hover,
.split-resizer.dragging {
background: var(--accent);
}
.list-pane {
width: 300px;
flex-shrink: 0;
@@ -199,6 +220,7 @@ nav {
top: calc(3rem + 1.5rem); /* header + main padding */
max-height: calc(100vh - 3rem - 3rem);
overflow-y: auto;
margin-right: 0.625rem;
}
.quick-add-input {
@@ -236,6 +258,7 @@ nav {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
margin-left: 0.625rem;
}
/* ===== DETAIL PANEL ===== */
@@ -300,7 +323,7 @@ nav {
.detail-field-row {
display: grid;
grid-template-columns: 10rem 1fr;
grid-template-columns: auto 1fr;
gap: 0.5rem;
font-size: 0.9rem;
padding: 0.2rem 0;
@@ -310,11 +333,16 @@ nav {
color: var(--text-dim);
font-family: var(--font-mono);
font-size: 0.85rem;
overflow-wrap: break-word;
min-width: 0;
max-width: 14rem;
}
.detail-field-value {
font-family: var(--font-mono);
font-size: 0.85rem;
min-width: 0;
overflow-wrap: break-word;
}
.detail-empty-note {
@@ -351,7 +379,6 @@ nav {
border-radius: 4px;
border: 1px solid var(--border);
display: block;
cursor: pointer;
transition: border-color 0.1s;
}
@@ -450,15 +477,15 @@ nav {
gap: 0.4rem;
}
.cell-thumb-link {
.cell-thumb-preview {
display: block;
border: 2px solid var(--accent, #5b9cf6);
border-radius: 3px;
overflow: hidden;
flex-shrink: 0;
cursor: zoom-in;
}
.cell-thumb-link .thumb-img {
.cell-thumb-preview .thumb-img {
display: block;
width: 128px;
height: 128px;
@@ -854,14 +881,18 @@ nav {
}
/* Field input rows inside component dialog */
.c-field-input-row {
#c-field-rows {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: 0.5rem;
grid-template-columns: minmax(0, max-content) minmax(0, 1fr) auto;
gap: 0.35rem 0.6rem;
align-items: center;
margin-bottom: 0.5rem;
}
.c-field-input-row {
display: contents;
}
.c-field-input-label {
font-size: 0.85rem;
color: var(--text-dim);
@@ -869,6 +900,7 @@ nav {
display: flex;
align-items: baseline;
gap: 0.3rem;
white-space: nowrap;
}
.c-field-unit-hint {
@@ -1452,6 +1484,11 @@ nav {
gap: 2px;
}
.grid-cell.highlighted .grid-cell-img-wrap {
outline: 3px solid var(--accent, #5b9cf6);
outline-offset: 2px;
}
.grid-cell-img-wrap {
position: relative;
width: 100%;
@@ -1620,6 +1657,8 @@ nav {
border-radius: 3px;
padding: 0.1rem 0.25rem;
margin: 0 -0.25rem;
color: inherit;
text-decoration: none;
}
.cell-inv-item-link:hover {