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

View File

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

View File

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