Redesign toolbar, palette, and dock UI

Toolbar:
- Compact project/MCM selectors always visible at top
- Action bar (SVG, JSON, Import, Clear) as compact button strip
- Cleaner section toggles with minimal chrome
- Inline settings rows (W/H and Grid/Gap on one line each)
- Narrower width (220px vs 240px)

Palette:
- Group headers are collapsible (start collapsed to save space)
- Small symbols (Controls, Sensors, I/O, Other) use 2-column grid
- Conveyance keeps list layout (wider items)
- Lighter, less cluttered item styling
- Custom scrollbar

Color scheme:
- Shifted from navy/red (#16213e/#e94560) to slate/blue (#111827/#3b82f6)
- Better contrast and more professional feel
- DeviceDock updated to match

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
igurielidze 2026-03-30 20:49:45 +04:00
parent 1e67c3de47
commit e3a0e422e6
3 changed files with 409 additions and 353 deletions

View File

@ -143,8 +143,8 @@
.device-dock {
width: 200px;
min-width: 200px;
background: #16213e;
border-left: 2px solid #0f3460;
background: #111827;
border-left: 1px solid #1f2937;
display: flex;
flex-direction: column;
position: relative;
@ -163,8 +163,8 @@
left: 4px;
width: 24px;
height: 24px;
background: #0f3460;
border: 1px solid #1a1a5e;
background: #1f2937;
border: 1px solid #374151;
color: #e0e0e0;
border-radius: 4px;
cursor: pointer;
@ -177,7 +177,7 @@
}
.collapse-btn:hover {
background: #e94560;
background: #3b82f6;
}
.dock-content {
@ -230,8 +230,8 @@
.search-box input {
width: 100%;
padding: 5px 22px 5px 8px;
background: #0f3460;
border: 1px solid #1a1a5e;
background: #1f2937;
border: 1px solid #374151;
color: #e0e0e0;
border-radius: 4px;
font-size: 11px;
@ -241,7 +241,7 @@
}
.search-box input:focus {
border-color: #e94560;
border-color: #3b82f6;
}
.search-box input::placeholder {
@ -268,7 +268,7 @@
}
.clear-search:hover {
color: #e94560;
color: #3b82f6;
}
.dock-empty {
@ -291,8 +291,8 @@
width: 100%;
padding: 4px 6px;
margin-bottom: 1px;
background: #0d2847;
border: 1px solid #1a1a5e;
background: #1f2937;
border: 1px solid #374151;
border-radius: 4px;
color: #8899aa;
font-size: 9px;
@ -305,7 +305,7 @@
}
.zone-toggle:hover {
color: #e94560;
color: #3b82f6;
}
.zone-count {
@ -342,8 +342,8 @@
gap: 5px;
width: 100%;
padding: 3px 6px;
background: #0f3460;
border: 1px solid #1a1a5e;
background: #1f2937;
border: 1px solid #374151;
border-radius: 3px;
cursor: grab;
user-select: none;
@ -353,8 +353,8 @@
}
.device-item:hover {
background: #1a1a5e;
border-color: #e94560;
background: #374151;
border-color: #3b82f6;
transform: translateX(-2px);
}

View File

@ -32,55 +32,64 @@
};
});
let expandedSubgroups = $state<Set<string>>(new Set());
let expandedGroups = $state<Set<string>>(new Set());
function toggleSubgroup(key: string) {
const next = new Set(expandedSubgroups);
function toggleGroup(key: string) {
const next = new Set(expandedGroups);
if (next.has(key)) next.delete(key);
else next.add(key);
expandedSubgroups = next;
expandedGroups = next;
}
// Small symbols use grid layout (2 columns)
const GRID_GROUPS = new Set(['I/O Modules', 'Sensors', 'Controls', 'Other']);
</script>
<div class="palette">
{#each grouped as group (group.name)}
<div class="group">
<div class="group-header">{group.name}</div>
{@const isGrid = GRID_GROUPS.has(group.name)}
{@const groupKey = group.name}
<!-- Group header (collapsible) -->
<button class="group-header" onclick={() => toggleGroup(groupKey)}>
<span class="chevron" class:open={expandedGroups.has(groupKey)}></span>
{group.name}
<span class="count">{group.subgroups.reduce((n, s) => n + s.symbols.length, 0) + group.symbols.length}</span>
</button>
{#if expandedGroups.has(groupKey)}
<!-- Subgroups -->
{#if group.subgroups.length > 0}
{#each group.subgroups as sub (sub.name)}
{@const key = `${group.name}/${sub.name}`}
<button class="subgroup-header" onclick={() => toggleSubgroup(key)}>
<span class="chevron" class:open={expandedSubgroups.has(key)}></span>
{sub.name}
</button>
{#if expandedSubgroups.has(key)}
<div class="group-items subgroup-items">
{#each sub.symbols as sym (sym.id)}
<button
class="palette-item"
title={sym.name}
onmousedown={(e) => onMousedown(e, sym)}
>
<img
src={sym.file}
alt={sym.name}
draggable="false"
style={sym.defaultRotation ? `transform: rotate(${sym.defaultRotation}deg)` : ''}
/>
<span>{sym.name}</span>
</button>
{/each}
</div>
{/if}
<div class="sub-label">{sub.name}</div>
<div class="items" class:grid={isGrid}>
{#each sub.symbols as sym (sym.id)}
<button
class="item"
class:grid-item={isGrid}
title={sym.name}
onmousedown={(e) => onMousedown(e, sym)}
>
<img
src={sym.file}
alt={sym.name}
draggable="false"
style={sym.defaultRotation ? `transform: rotate(${sym.defaultRotation}deg)` : ''}
/>
<span class="item-name">{sym.name}</span>
</button>
{/each}
</div>
{/each}
{/if}
<!-- Ungrouped symbols -->
{#if group.symbols.length > 0}
<div class="group-items">
<div class="items" class:grid={isGrid}>
{#each group.symbols as sym (sym.id)}
<button
class="palette-item"
class="item"
class:grid-item={isGrid}
title={sym.name}
onmousedown={(e) => onMousedown(e, sym)}
>
@ -90,12 +99,12 @@
draggable="false"
style={sym.defaultRotation ? `transform: rotate(${sym.defaultRotation}deg)` : ''}
/>
<span>{sym.name}</span>
<span class="item-name">{sym.name}</span>
</button>
{/each}
</div>
{/if}
</div>
{/if}
{/each}
</div>
@ -103,113 +112,156 @@
.palette {
flex: 1;
overflow-y: auto;
padding: 0 2px;
min-height: 0;
padding-bottom: 40px;
}
.group {
margin-bottom: 6px;
/* Scrollbar */
.palette::-webkit-scrollbar {
width: 4px;
}
.palette::-webkit-scrollbar-track {
background: transparent;
}
.palette::-webkit-scrollbar-thumb {
background: #374151;
border-radius: 2px;
}
.group-header {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
padding: 6px 4px;
background: none;
border: none;
border-bottom: 1px solid #1f2937;
color: #e5e7eb;
font-size: 10px;
color: #e94560;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 600;
padding: 4px 6px 2px;
letter-spacing: 0.8px;
cursor: pointer;
transition: color 0.15s;
text-align: left;
position: sticky;
top: 0;
background: #16213e;
background: #111827;
z-index: 1;
}
.subgroup-header {
display: flex;
align-items: center;
gap: 4px;
width: 100%;
padding: 3px 6px 3px 12px;
background: none;
border: none;
color: #8899aa;
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
cursor: pointer;
user-select: none;
.group-header:hover {
color: #3b82f6;
}
.subgroup-header:hover {
color: #e94560;
.count {
margin-left: auto;
font-size: 9px;
color: #4b5563;
font-weight: 400;
}
.chevron {
display: inline-block;
width: 0;
height: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 5px solid currentColor;
border-top: 3.5px solid transparent;
border-bottom: 3.5px solid transparent;
border-left: 4.5px solid currentColor;
transition: transform 0.15s ease;
flex-shrink: 0;
}
.chevron.open {
transform: rotate(90deg);
}
.subgroup-items {
padding-left: 8px;
.sub-label {
font-size: 9px;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.3px;
font-weight: 600;
padding: 4px 4px 2px 14px;
}
.group-items {
/* List layout (conveyance) */
.items {
display: flex;
flex-direction: column;
gap: 1px;
padding: 0 2px 2px;
}
/* Grid layout (small symbols) */
.items.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2px;
}
.palette-item {
.item {
display: flex;
align-items: center;
gap: 6px;
gap: 5px;
width: 100%;
padding: 4px 6px;
background: #0f3460;
border: 1px solid #1a1a5e;
border-radius: 4px;
padding: 3px 5px;
background: transparent;
border: 1px solid transparent;
border-radius: 3px;
cursor: grab;
user-select: none;
transition: all 0.15s ease;
color: #ccc;
transition: all 0.12s ease;
color: #9ca3af;
text-align: left;
}
.palette-item:hover {
background: #1a1a5e;
border-color: #e94560;
transform: translateX(2px);
.item:hover {
background: #1f2937;
border-color: #374151;
color: #e5e7eb;
}
.palette-item:active {
.item:active {
cursor: grabbing;
transform: scale(0.98);
background: #374151;
}
.palette-item img {
width: 36px;
height: 24px;
/* Grid items are more compact */
.item.grid-item {
flex-direction: column;
align-items: center;
gap: 2px;
padding: 4px 2px 3px;
}
.item.grid-item .item-name {
text-align: center;
font-size: 8px;
}
.item img {
width: 28px;
height: 18px;
object-fit: contain;
background: transparent;
border-radius: 3px;
padding: 2px;
flex-shrink: 0;
filter: invert(1);
opacity: 0.8;
}
.palette-item span {
.item:hover img {
opacity: 1;
}
.item.grid-item img {
width: 24px;
height: 20px;
}
.item-name {
font-size: 10px;
line-height: 1.2;
line-height: 1.1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

View File

@ -10,7 +10,6 @@
let pdfFileEl = $state<HTMLInputElement>(null!);
let toolbarCollapsed = $state(false);
let projectOpen = $state(false);
let pdfOpen = $state(false);
let settingsOpen = $state(false);
@ -66,23 +65,10 @@
await loadPdfFromPath(mcm.pdfPath);
}
function onCanvasSizeChange() {
layout.markDirty();
layout.saveMcmState();
}
function onGridSizeChange() {
layout.markDirty();
layout.saveMcmState();
}
function onShowGridChange() {
layout.markDirty();
}
function onMinSpacingChange() {
layout.saveMcmState();
}
function onCanvasSizeChange() { layout.markDirty(); layout.saveMcmState(); }
function onGridSizeChange() { layout.markDirty(); layout.saveMcmState(); }
function onShowGridChange() { layout.markDirty(); }
function onMinSpacingChange() { layout.saveMcmState(); }
function clearCanvas() {
if (!confirm('Clear all placed symbols?')) return;
@ -93,11 +79,8 @@
const file = (e.target as HTMLInputElement).files?.[0];
if (!file) return;
try {
if (file.name.endsWith('.svg')) {
await loadLayoutSVG(file);
} else {
await loadLayoutJSON(file);
}
if (file.name.endsWith('.svg')) await loadLayoutSVG(file);
else await loadLayoutJSON(file);
} catch (err) {
alert('Invalid layout file: ' + (err instanceof Error ? err.message : String(err)));
}
@ -118,141 +101,104 @@
<div class="toolbar" class:collapsed={toolbarCollapsed}>
<button class="collapse-btn" onclick={() => toolbarCollapsed = !toolbarCollapsed}>
{toolbarCollapsed ? '>' : '<'}
{toolbarCollapsed ? '\u276F' : '\u276E'}
</button>
{#if !toolbarCollapsed}
<div class="toolbar-content">
<!-- Project section (collapsible) -->
<button class="section-toggle" onclick={() => projectOpen = !projectOpen}>
<span class="toggle-arrow">{projectOpen ? '\u25BC' : '\u25B6'}</span>
Project
<span class="section-summary">{layout.currentProject || '...'} / {layout.currentMcm || '...'}</span>
</button>
{#if projectOpen}
<div class="section-body">
{#if layout.projects.length > 0}
<div class="setting">
<label for="projectSelect">Project:</label>
<select id="projectSelect" value={layout.currentProject} onchange={onProjectChange}>
{#each layout.projects as proj (proj.name)}
<option value={proj.name}>{proj.name}</option>
{/each}
</select>
</div>
<div class="setting">
<label for="mcmSelect">MCM:</label>
<select id="mcmSelect" value={layout.currentMcm} onchange={onMcmChange}>
{#each currentMcms as mcm (mcm.name)}
<option value={mcm.name}>{mcm.name}</option>
{/each}
</select>
</div>
{:else}
<div class="setting">
<label for="mcmSelect">MCM:</label>
<select id="mcmSelect" value={layout.currentMcm} onchange={onMcmChange}>
<option value="MCM08">MCM08</option>
</select>
</div>
{/if}
</div>
{/if}
<!-- Project & MCM selectors — always visible, compact -->
<div class="project-bar">
{#if layout.projects.length > 0}
<select class="proj-select" value={layout.currentProject} onchange={onProjectChange} title="Project">
{#each layout.projects as proj (proj.name)}
<option value={proj.name}>{proj.name}</option>
{/each}
</select>
{/if}
<select class="mcm-select" value={layout.currentMcm} onchange={onMcmChange} title="MCM">
{#each currentMcms as mcm (mcm.name)}
<option value={mcm.name}>{mcm.name}</option>
{/each}
</select>
</div>
<!-- Background PDF section (collapsible) -->
<!-- Action bar — always visible -->
<div class="action-bar">
<button onclick={exportSVG} title="Export SVG">SVG</button>
<button onclick={exportJSON} title="Export JSON">JSON</button>
<button onclick={() => importFileEl.click()} title="Import layout file">Import</button>
<button class="btn-danger" onclick={clearCanvas} title="Clear canvas">Clear</button>
<input bind:this={importFileEl} type="file" accept=".json,.svg" style="display:none" onchange={onImportFile}>
</div>
<div class="divider"></div>
<!-- Background PDF -->
<button class="section-toggle" onclick={() => pdfOpen = !pdfOpen}>
<span class="toggle-arrow">{pdfOpen ? '\u25BC' : '\u25B6'}</span>
Background PDF
<span class="chevron" class:open={pdfOpen}></span>
Background
{#if layout.pdfLoaded}
<span class="section-summary">{Math.round(layout.pdfScale * 100)}%</span>
<span class="badge">{Math.round(layout.pdfScale * 100)}%</span>
{/if}
</button>
{#if pdfOpen}
<div class="section-body">
<div class="setting btn-row">
<div class="btn-row">
<button onclick={() => pdfFileEl.click()}>Load PDF</button>
<input bind:this={pdfFileEl} type="file" accept=".pdf" style="display:none" onchange={onPdfFile}>
</div>
<div class="setting btn-row">
<button onclick={pdfZoomIn}>PDF +</button>
<button onclick={pdfZoomOut}>PDF -</button>
</div>
<div class="setting btn-row">
<button
class:edit-bg-active={layout.editingBackground}
onclick={toggleEditBackground}
>
{layout.editingBackground ? 'Done' : 'Edit BG'}
</button>
<button onclick={removePdf}>Remove</button>
</div>
{#if layout.pdfLoaded}
<div class="pdf-info">
scale {Math.round(layout.pdfScale * 100)}% pos: {Math.round(layout.pdfOffsetX)},{Math.round(layout.pdfOffsetY)}
<div class="btn-row">
<button onclick={pdfZoomOut}>-</button>
<span class="zoom-label">{Math.round(layout.pdfScale * 100)}%</span>
<button onclick={pdfZoomIn}>+</button>
<button
class:active={layout.editingBackground}
onclick={toggleEditBackground}
>
{layout.editingBackground ? 'Done' : 'Move'}
</button>
</div>
{/if}
</div>
{/if}
<!-- Settings section (collapsible) -->
<!-- Canvas Settings -->
<button class="section-toggle" onclick={() => settingsOpen = !settingsOpen}>
<span class="toggle-arrow">{settingsOpen ? '\u25BC' : '\u25B6'}</span>
Settings
<span class="chevron" class:open={settingsOpen}></span>
Canvas
</button>
{#if settingsOpen}
<div class="section-body">
<div class="setting canvas-size-row">
<div>
<label for="canvasW">W:</label>
<input type="number" id="canvasW" bind:value={layout.canvasW} min="800" max="7680" step="10" onchange={onCanvasSizeChange}>
</div>
<div>
<label for="canvasH">H:</label>
<input type="number" id="canvasH" bind:value={layout.canvasH} min="600" max="4320" step="10" onchange={onCanvasSizeChange}>
</div>
<div class="inline-row">
<label>W</label>
<input type="number" bind:value={layout.canvasW} min="800" max="7680" step="10" onchange={onCanvasSizeChange}>
<label>H</label>
<input type="number" bind:value={layout.canvasH} min="600" max="4320" step="10" onchange={onCanvasSizeChange}>
</div>
<div class="setting">
<label for="gridSize">Grid Size (px):</label>
<input
type="number" id="gridSize"
bind:value={layout.gridSize}
min="5" max="100" step="5"
onchange={onGridSizeChange}
>
<div class="inline-row">
<label>Grid</label>
<input type="number" bind:value={layout.gridSize} min="5" max="100" step="5" onchange={onGridSizeChange}>
<label>Gap</label>
<input type="number" bind:value={layout.minSpacing} min="0" max="100" step="5" onchange={onMinSpacingChange}>
</div>
<div class="setting">
<label for="minSpacing">Min Edge Spacing (px):</label>
<input
type="number" id="minSpacing"
bind:value={layout.minSpacing}
min="0" max="100" step="5"
onchange={onMinSpacingChange}
>
</div>
<div class="setting">
<label class="checkbox-label">
<div class="toggle-row">
<label class="toggle-label">
<input type="checkbox" bind:checked={layout.showGrid} onchange={onShowGridChange}>
Show Grid
<span>Grid</span>
</label>
</div>
<div class="setting">
<label class="checkbox-label">
<label class="toggle-label">
<input type="checkbox" bind:checked={layout.snapEnabled}>
Snap to Grid
<span>Snap</span>
</label>
</div>
<div class="setting btn-row">
<button onclick={exportSVG}>Save SVG</button>
<button onclick={exportJSON}>Save JSON</button>
<button onclick={() => importFileEl.click()}>Load JSON</button>
<button onclick={clearCanvas}>Clear</button>
<input bind:this={importFileEl} type="file" accept=".json,.svg" style="display:none" onchange={onImportFile}>
</div>
</div>
{/if}
<!-- Symbols section (always open, takes remaining space) -->
<div class="symbols-header">Symbols</div>
<div class="divider"></div>
<!-- Symbols -->
<Palette />
</div>
{/if}
@ -260,10 +206,10 @@
<style>
.toolbar {
width: 240px;
min-width: 240px;
background: #16213e;
border-right: 2px solid #0f3460;
width: 220px;
min-width: 220px;
background: #111827;
border-right: 1px solid #1f2937;
display: flex;
flex-direction: column;
position: relative;
@ -272,35 +218,36 @@
}
.toolbar.collapsed {
width: 32px;
min-width: 32px;
width: 28px;
min-width: 28px;
}
.collapse-btn {
position: absolute;
top: 8px;
right: 4px;
width: 24px;
height: 24px;
background: #0f3460;
border: 1px solid #1a1a5e;
color: #e0e0e0;
border-radius: 4px;
top: 6px;
right: 2px;
width: 22px;
height: 22px;
background: transparent;
border: none;
color: #6b7280;
cursor: pointer;
z-index: 10;
font-size: 12px;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s;
border-radius: 4px;
transition: all 0.15s;
}
.collapse-btn:hover {
background: #e94560;
background: #1f2937;
color: #e5e7eb;
}
.toolbar-content {
padding: 6px;
padding: 4px 6px;
overflow: hidden;
flex: 1;
display: flex;
@ -308,165 +255,222 @@
min-height: 0;
}
/* Collapsible section toggle */
/* Project bar */
.project-bar {
display: flex;
gap: 3px;
margin-bottom: 4px;
flex-shrink: 0;
}
.proj-select, .mcm-select {
flex: 1;
padding: 4px 6px;
background: #1f2937;
border: 1px solid #374151;
color: #e5e7eb;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
cursor: pointer;
outline: none;
appearance: none;
min-width: 0;
}
.proj-select:focus, .mcm-select:focus {
border-color: #3b82f6;
}
/* Action bar */
.action-bar {
display: flex;
gap: 2px;
margin-bottom: 4px;
flex-shrink: 0;
}
.action-bar button {
flex: 1;
padding: 4px 2px;
background: #1f2937;
border: 1px solid #374151;
color: #9ca3af;
border-radius: 4px;
cursor: pointer;
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
transition: all 0.15s;
}
.action-bar button:hover {
background: #374151;
color: #e5e7eb;
}
.action-bar .btn-danger:hover {
background: #991b1b;
border-color: #991b1b;
color: #fca5a5;
}
.divider {
height: 1px;
background: #1f2937;
margin: 2px 0;
flex-shrink: 0;
}
/* Section toggle */
.section-toggle {
display: flex;
align-items: center;
gap: 4px;
gap: 6px;
width: 100%;
padding: 5px 6px;
margin-bottom: 2px;
background: #0d2847;
border: 1px solid #1a1a5e;
border-radius: 4px;
color: #e94560;
padding: 5px 4px;
background: none;
border: none;
color: #9ca3af;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
letter-spacing: 0.5px;
cursor: pointer;
transition: background 0.15s;
transition: color 0.15s;
text-align: left;
flex-shrink: 0;
}
.section-toggle:hover {
background: #122d52;
color: #e5e7eb;
}
.toggle-arrow {
font-size: 8px;
width: 10px;
.chevron {
display: inline-block;
width: 0;
height: 0;
border-top: 3.5px solid transparent;
border-bottom: 3.5px solid transparent;
border-left: 4.5px solid currentColor;
transition: transform 0.15s ease;
flex-shrink: 0;
}
.section-summary {
.chevron.open {
transform: rotate(90deg);
}
.badge {
margin-left: auto;
font-size: 9px;
color: #667;
color: #6b7280;
font-weight: 400;
text-transform: none;
letter-spacing: 0;
background: #1f2937;
padding: 1px 5px;
border-radius: 8px;
}
.section-body {
padding: 4px 2px 6px;
padding: 2px 0 6px 10px;
flex-shrink: 0;
}
/* Symbols header (always visible) */
.symbols-header {
font-size: 10px;
color: #e94560;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 600;
padding: 5px 6px 3px;
flex-shrink: 0;
}
.setting {
margin-bottom: 4px;
}
.setting label {
display: block;
font-size: 10px;
margin-bottom: 2px;
color: #8899aa;
}
.checkbox-label {
display: flex !important;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 11px !important;
color: #ccc !important;
}
.setting input[type="number"],
.setting select {
width: 100%;
padding: 4px 6px;
background: #0f3460;
border: 1px solid #1a1a5e;
color: #e0e0e0;
border-radius: 4px;
font-size: 12px;
transition: border-color 0.15s;
outline: none;
}
.setting input[type="number"]:focus,
.setting select:focus {
border-color: #e94560;
}
.setting select {
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238899aa'%3E%3Cpath d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 6px center;
padding-right: 24px;
}
.setting input[type="checkbox"] {
accent-color: #e94560;
width: 13px;
height: 13px;
}
/* Button rows */
.btn-row {
display: flex;
gap: 3px;
margin-bottom: 3px;
align-items: center;
}
.btn-row button,
.setting > button {
.btn-row button, .section-body > button {
flex: 1;
padding: 5px 6px;
background: #0f3460;
border: 1px solid #1a1a5e;
color: #e0e0e0;
padding: 4px 6px;
background: #1f2937;
border: 1px solid #374151;
color: #9ca3af;
border-radius: 4px;
cursor: pointer;
font-size: 10px;
font-weight: 500;
transition: all 0.15s ease;
white-space: nowrap;
transition: all 0.15s;
}
.btn-row button:hover,
.setting > button:hover {
background: #e94560;
border-color: #e94560;
.btn-row button:hover {
background: #374151;
color: #e5e7eb;
}
.btn-row button:active,
.setting > button:active {
transform: translateY(0);
.btn-row button.active {
background: #3b82f6;
border-color: #3b82f6;
color: #fff;
}
.edit-bg-active {
background: #e94560 !important;
border-color: #e94560 !important;
color: #fff !important;
.zoom-label {
font-size: 10px;
color: #9ca3af;
min-width: 32px;
text-align: center;
}
.canvas-size-row {
/* Inline settings rows */
.inline-row {
display: flex;
align-items: center;
gap: 4px;
margin-bottom: 3px;
}
.canvas-size-row > div {
flex: 1;
}
.pdf-info {
.inline-row label {
font-size: 9px;
color: #667;
padding: 2px 0;
color: #6b7280;
font-weight: 600;
text-transform: uppercase;
flex-shrink: 0;
min-width: 14px;
}
.inline-row input[type="number"] {
flex: 1;
padding: 3px 4px;
background: #1f2937;
border: 1px solid #374151;
color: #e5e7eb;
border-radius: 4px;
font-size: 11px;
outline: none;
min-width: 0;
}
.inline-row input:focus {
border-color: #3b82f6;
}
.toggle-row {
display: flex;
gap: 12px;
}
.toggle-label {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
font-size: 10px;
color: #9ca3af;
}
.toggle-label input[type="checkbox"] {
accent-color: #3b82f6;
width: 12px;
height: 12px;
}
.toggle-label span {
user-select: none;
}
</style>