Group toolbar items into visual pill containers
Four groups: connection, quad view, themes, panel config. Each group shares one background pill. Bigger icons (w-6 h-6), 36px hit area with minimal padding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f8ef8141f5
commit
4abbe86963
@ -1,93 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isLoggedIn" class="app-toolbar">
|
<div v-if="isLoggedIn" class="app-toolbar">
|
||||||
<!-- Connection pill -->
|
|
||||||
<button class="toolbar-pill" :class="{ active: connActive }" @click="toggleDropdown('conn')" :title="connLabel || 'Connection'">
|
<!-- Group: Connection + Takeover -->
|
||||||
<WifiIcon class="w-4 h-4" />
|
<div class="toolbar-group">
|
||||||
<span v-if="connLabel" class="toolbar-pill-label">{{ connLabel }}</span>
|
<button class="toolbar-btn" :class="{ active: connActive }" @click="toggleDropdown('conn')" :title="connLabel || 'Connection'">
|
||||||
</button>
|
<WifiIcon class="w-6 h-6" />
|
||||||
|
<span v-if="connLabel" class="toolbar-btn-label">{{ connLabel }}</span>
|
||||||
|
</button>
|
||||||
|
<button v-if="takeoverToken" class="toolbar-btn" :class="{ active: captureActive }" @click="toggleDropdown('takeover')" title="Takeover">
|
||||||
|
<SignalIcon class="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="toolbar-spacer" />
|
<div class="toolbar-spacer" />
|
||||||
|
|
||||||
<!-- Pane toggles -->
|
<!-- Group: Quad view (pane toggles) -->
|
||||||
<button
|
<div class="toolbar-group">
|
||||||
class="toolbar-pill toolbar-pill-icon"
|
<button class="toolbar-btn" :class="{ active: isPaneOpen('chat') }" @click="togglePane('chat')" title="Chat">
|
||||||
:class="{ active: isPaneOpen('chat') }"
|
<ChatBubbleLeftIcon class="w-6 h-6" />
|
||||||
@click="togglePane('chat')"
|
</button>
|
||||||
title="Chat"
|
<button class="toolbar-btn" :class="{ active: isPaneOpen('dashboard') }" @click="togglePane('dashboard')" title="Display">
|
||||||
>
|
<RectangleGroupIcon class="w-6 h-6" />
|
||||||
<ChatBubbleLeftIcon class="w-4 h-4" />
|
</button>
|
||||||
</button>
|
<button class="toolbar-btn" :class="{ active: isPaneOpen('files') }" @click="togglePane('files')" title="Files">
|
||||||
<button
|
<FolderIcon class="w-6 h-6" />
|
||||||
class="toolbar-pill toolbar-pill-icon"
|
</button>
|
||||||
:class="{ active: isPaneOpen('dashboard') }"
|
<button class="toolbar-btn" :class="{ active: isPaneOpen('artifacts') }" @click="togglePane('artifacts')" title="Konsole">
|
||||||
@click="togglePane('dashboard')"
|
<SparklesIcon class="w-6 h-6" />
|
||||||
title="Display"
|
</button>
|
||||||
>
|
</div>
|
||||||
<RectangleGroupIcon class="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="toolbar-pill toolbar-pill-icon"
|
|
||||||
:class="{ active: isPaneOpen('files') }"
|
|
||||||
@click="togglePane('files')"
|
|
||||||
title="Files"
|
|
||||||
>
|
|
||||||
<FolderIcon class="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="toolbar-pill toolbar-pill-icon"
|
|
||||||
:class="{ active: isPaneOpen('artifacts') }"
|
|
||||||
@click="togglePane('artifacts')"
|
|
||||||
title="Konsole"
|
|
||||||
>
|
|
||||||
<SparklesIcon class="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="toolbar-divider" />
|
<!-- Group: Themes -->
|
||||||
|
<div class="toolbar-group">
|
||||||
|
<button
|
||||||
|
v-for="t in THEMES"
|
||||||
|
:key="t"
|
||||||
|
class="toolbar-btn"
|
||||||
|
:class="{ active: theme === t }"
|
||||||
|
@click="setTheme(t)"
|
||||||
|
:title="THEME_NAMES[t]"
|
||||||
|
>
|
||||||
|
<component :is="THEME_ICONS[t]" class="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Theme pills (icon only) -->
|
<!-- Group: Panel config + version -->
|
||||||
<button
|
<div class="toolbar-group">
|
||||||
v-for="t in THEMES"
|
<button
|
||||||
:key="t"
|
v-for="panel in availablePanels"
|
||||||
class="toolbar-pill toolbar-pill-icon"
|
:key="panel.id"
|
||||||
:class="{ active: theme === t }"
|
class="toolbar-btn"
|
||||||
@click="setTheme(t)"
|
:class="{ active: isPanelOpen(panel.id) }"
|
||||||
:title="THEME_NAMES[t]"
|
@click="togglePanelBtn(panel.id)"
|
||||||
>
|
:title="panel.label"
|
||||||
<component :is="THEME_ICONS[t]" class="w-4 h-4" />
|
>
|
||||||
</button>
|
<component :is="panelIcon(panel.id)" class="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
<button class="toolbar-btn toolbar-btn-dim" @click="copyVersionDetails" :title="versionFull">
|
||||||
|
<span class="toolbar-version-text">{{ versionShort }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Panel toggles (dev/operator tools) -->
|
<!-- Dropdowns -->
|
||||||
<button
|
|
||||||
v-for="panel in availablePanels"
|
|
||||||
:key="panel.id"
|
|
||||||
class="toolbar-pill toolbar-pill-icon"
|
|
||||||
:class="{ active: isPanelOpen(panel.id) }"
|
|
||||||
@click="togglePanelBtn(panel.id)"
|
|
||||||
:title="panel.label"
|
|
||||||
>
|
|
||||||
<component :is="panelIcon(panel.id)" class="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Takeover / Capture -->
|
|
||||||
<button
|
|
||||||
v-if="takeoverToken"
|
|
||||||
class="toolbar-pill"
|
|
||||||
:class="{ active: captureActive }"
|
|
||||||
@click="toggleDropdown('takeover')"
|
|
||||||
title="Takeover"
|
|
||||||
>
|
|
||||||
<SignalIcon class="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Version pill -->
|
|
||||||
<button class="toolbar-pill toolbar-pill-dim" @click="copyVersionDetails" :title="versionFull">
|
|
||||||
<span class="toolbar-version-text">{{ versionShort }}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Panels (dropdown from toolbar) -->
|
|
||||||
<div v-if="openDropdown" class="toolbar-panel-backdrop" @click="openDropdown = null" />
|
<div v-if="openDropdown" class="toolbar-panel-backdrop" @click="openDropdown = null" />
|
||||||
|
|
||||||
<!-- Connection panel -->
|
|
||||||
<div v-if="openDropdown === 'conn'" class="toolbar-panel" style="right: auto; left: 0;">
|
<div v-if="openDropdown === 'conn'" class="toolbar-panel" style="right: auto; left: 0;">
|
||||||
<div class="toolbar-panel-header">Connection</div>
|
<div class="toolbar-panel-header">Connection</div>
|
||||||
<div class="toolbar-panel-row"><span>HTTP</span><span>{{ chatStore.connectionState }}</span></div>
|
<div class="toolbar-panel-row"><span>HTTP</span><span>{{ chatStore.connectionState }}</span></div>
|
||||||
@ -96,7 +72,6 @@
|
|||||||
<div class="toolbar-panel-row"><span>Mode</span><span>{{ selectedMode }}</span></div>
|
<div class="toolbar-panel-row"><span>Mode</span><span>{{ selectedMode }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Takeover panel -->
|
|
||||||
<div v-if="openDropdown === 'takeover'" class="toolbar-panel" style="right: 0;">
|
<div v-if="openDropdown === 'takeover'" class="toolbar-panel" style="right: 0;">
|
||||||
<div class="toolbar-panel-header">Takeover</div>
|
<div class="toolbar-panel-header">Takeover</div>
|
||||||
<div class="toolbar-panel-token" @click="copyToken" :title="tokenCopied ? 'Copied!' : 'Click to copy'">
|
<div class="toolbar-panel-token" @click="copyToken" :title="tokenCopied ? 'Copied!' : 'Click to copy'">
|
||||||
@ -107,11 +82,10 @@
|
|||||||
<span>Capture</span>
|
<span>Capture</span>
|
||||||
<span :style="{ color: captureActive ? 'var(--success, #22c55e)' : 'var(--text-dim)' }">{{ captureActive ? 'ON' : 'OFF' }}</span>
|
<span :style="{ color: captureActive ? 'var(--success, #22c55e)' : 'var(--text-dim)' }">{{ captureActive ? 'ON' : 'OFF' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="toolbar-panel-action" @click="toggleCapture">
|
<button class="toolbar-panel-action" @click="toggleCapture">{{ captureActive ? 'Disable Capture' : 'Enable Capture' }}</button>
|
||||||
{{ captureActive ? 'Disable Capture' : 'Enable Capture' }}
|
|
||||||
</button>
|
|
||||||
<button class="toolbar-panel-action" @click="revokeAndClose">Revoke</button>
|
<button class="toolbar-panel-action" @click="revokeAndClose">Revoke</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -218,51 +192,57 @@ function toggleDropdown(panel: 'conn' | 'takeover') {
|
|||||||
|
|
||||||
.toolbar-spacer { flex: 1; }
|
.toolbar-spacer { flex: 1; }
|
||||||
|
|
||||||
.toolbar-divider {
|
/* Group: shared pill container */
|
||||||
width: 1px;
|
.toolbar-group {
|
||||||
height: 18px;
|
|
||||||
background: var(--border);
|
|
||||||
opacity: 0.5;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-pill {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
|
||||||
background: var(--panel-bg);
|
background: var(--panel-bg);
|
||||||
border-radius: var(--radius-panel, 12px);
|
border-radius: var(--radius-panel, 12px);
|
||||||
box-shadow: var(--panel-shadow);
|
box-shadow: var(--panel-shadow);
|
||||||
padding: 0 12px;
|
padding: 2px;
|
||||||
height: 34px;
|
gap: 1px;
|
||||||
font-size: 0.85rem;
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button inside a group */
|
||||||
|
.toolbar-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 5px;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: calc(var(--radius-panel, 12px) - 3px);
|
||||||
color: var(--text-dim);
|
color: var(--text-dim);
|
||||||
border: none;
|
border: none;
|
||||||
|
background: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 0.12s, background 0.12s;
|
font-size: 0.85rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
transition: color 0.12s, background 0.12s;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.toolbar-pill:hover { color: var(--text); }
|
.toolbar-btn:hover { color: var(--text); background: color-mix(in srgb, var(--text) 6%, transparent); }
|
||||||
.toolbar-pill.active { color: var(--accent); }
|
.toolbar-btn.active { color: var(--accent); }
|
||||||
|
|
||||||
.toolbar-pill-icon {
|
/* Connection button widens when it has a label */
|
||||||
width: 34px;
|
.toolbar-btn:has(.toolbar-btn-label) {
|
||||||
padding: 0;
|
width: auto;
|
||||||
justify-content: center;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
.toolbar-btn-label {
|
||||||
.toolbar-pill-dim {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
.toolbar-pill-dim:hover { opacity: 0.8; }
|
|
||||||
|
|
||||||
.toolbar-pill-label {
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-version-text {
|
.toolbar-btn-dim { opacity: 0.45; }
|
||||||
font-size: 0.65rem;
|
.toolbar-btn-dim:hover { opacity: 0.8; }
|
||||||
|
|
||||||
|
.toolbar-version-text { font-size: 0.65rem; }
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.toolbar-btn-label { display: none; }
|
||||||
|
.toolbar-btn:has(.toolbar-btn-label) { width: 32px; padding: 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Panel dropdown */
|
/* Panel dropdown */
|
||||||
@ -334,9 +314,7 @@ function toggleDropdown(panel: 'conn' | 'takeover') {
|
|||||||
}
|
}
|
||||||
.toolbar-panel-action:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); color: var(--accent); }
|
.toolbar-panel-action:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); color: var(--accent); }
|
||||||
|
|
||||||
/* Mobile: hide labels, shrink pills */
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.toolbar-pill-label { display: none; }
|
|
||||||
.toolbar-panel { width: min(220px, calc(100vw - 16px)); }
|
.toolbar-panel { width: min(220px, calc(100vw - 16px)); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user