Refactor: naming, type safety, consistency across panel system

- AppToolbar: rename togglePanel→toggleDropdown to avoid collision with
  debug panel toggle from usePanels
- ChatPane: computed()→toRef() for props passed to composables (consistent)
- ChatPane: add console.warn on session history fetch failure
- DebugColumn: skip object spread when no new panels resolved (perf)
- AgentsView: remove unused wsSwitchAgent, annotate disabled picker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nico 2026-04-02 20:21:20 +02:00
parent e948caf132
commit 4607a73d67
4 changed files with 24 additions and 20 deletions

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-if="isLoggedIn" class="app-toolbar"> <div v-if="isLoggedIn" class="app-toolbar">
<!-- Connection pill --> <!-- Connection pill -->
<button class="toolbar-pill" :class="{ active: connActive }" @click="togglePanel('conn')" :title="connLabel || 'Connection'"> <button class="toolbar-pill" :class="{ active: connActive }" @click="toggleDropdown('conn')" :title="connLabel || 'Connection'">
<WifiIcon class="w-4 h-4" /> <WifiIcon class="w-4 h-4" />
<span v-if="connLabel" class="toolbar-pill-label">{{ connLabel }}</span> <span v-if="connLabel" class="toolbar-pill-label">{{ connLabel }}</span>
</button> </button>
@ -57,7 +57,7 @@
v-if="takeoverToken" v-if="takeoverToken"
class="toolbar-pill" class="toolbar-pill"
:class="{ active: captureActive }" :class="{ active: captureActive }"
@click="togglePanel('takeover')" @click="toggleDropdown('takeover')"
title="Takeover" title="Takeover"
> >
<SignalIcon class="w-4 h-4" /> <SignalIcon class="w-4 h-4" />
@ -69,10 +69,10 @@
</button> </button>
<!-- Panels (dropdown from toolbar) --> <!-- Panels (dropdown from toolbar) -->
<div v-if="openPanel" class="toolbar-panel-backdrop" @click="openPanel = null" /> <div v-if="openDropdown" class="toolbar-panel-backdrop" @click="openDropdown = null" />
<!-- Connection panel --> <!-- Connection panel -->
<div v-if="openPanel === '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>
<div class="toolbar-panel-row"><span>Channel</span><span>{{ chatStore.channelState }}</span></div> <div class="toolbar-panel-row"><span>Channel</span><span>{{ chatStore.channelState }}</span></div>
@ -81,7 +81,7 @@
</div> </div>
<!-- Takeover panel --> <!-- Takeover panel -->
<div v-if="openPanel === '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'">
<code>{{ takeoverToken }}</code> <code>{{ takeoverToken }}</code>
@ -164,7 +164,7 @@ function copyToken() {
function revokeAndClose() { function revokeAndClose() {
takeover.revoke(); takeover.revoke();
openPanel.value = null; openDropdown.value = null;
} }
// Version // Version
@ -181,11 +181,11 @@ function copyVersionDetails() {
setTimeout(() => { versionCopied.value = false; }, 2000); setTimeout(() => { versionCopied.value = false; }, 2000);
} }
// Panel toggle // Toolbar dropdown (conn/takeover panels distinct from debug panel toggles)
const openPanel = ref<'conn' | 'takeover' | null>(null); const openDropdown = ref<'conn' | 'takeover' | null>(null);
function togglePanel(panel: 'conn' | 'takeover') { function toggleDropdown(panel: 'conn' | 'takeover') {
openPanel.value = openPanel.value === panel ? null : panel; openDropdown.value = openDropdown.value === panel ? null : panel;
} }
</script> </script>

View File

@ -259,7 +259,7 @@ async function fetchPreviousSession(loadMore = false) {
} }
prevSkip.value = skip + newSessions.length; prevSkip.value = skip + newSessions.length;
prevHasMore.value = data.hasMore ?? false; prevHasMore.value = data.hasMore ?? false;
} catch { /* silently fail */ } } catch (err) { console.warn('[chat] session history fetch failed:', err); }
finally { prevLoading.value = false; } finally { prevLoading.value = false; }
} }
@ -288,8 +288,8 @@ const visibleCount = ref(VISIBLE_PAGE);
const { groupedVisibleMsgs, hasMore, loadMore, getFormattedAgentName } = useMessageGrouping( const { groupedVisibleMsgs, hasMore, loadMore, getFormattedAgentName } = useMessageGrouping(
computed(() => chatStore.messages), computed(() => chatStore.messages),
visibleCount, visibleCount,
computed(() => props.selectedAgent), toRef(props, 'selectedAgent'),
computed(() => props.allAgents), toRef(props, 'allAgents'),
toRef(chatStore, 'sessionKey'), toRef(chatStore, 'sessionKey'),
); );
@ -421,7 +421,7 @@ function onConfirmNew() {
} }
const { inputEl, isShaking, autoGrow, triggerShake } = useInputAutogrow(input); const { inputEl, isShaking, autoGrow, triggerShake } = useInputAutogrow(input);
const { isAgentRunning } = useAgentDisplay(computed(() => props.selectedAgent), computed(() => props.defaultAgent), computed(() => props.allAgents)); const { isAgentRunning } = useAgentDisplay(toRef(props, 'selectedAgent'), toRef(props, 'defaultAgent'), toRef(props, 'allAgents'));
// Attachments // Attachments
const { attachments, addFiles, removeAttachment, clearAttachments, toPayload, hasAttachments } = useAttachments(); const { attachments, addFiles, removeAttachment, clearAttachments, toPayload, hasAttachments } = useAttachments();

View File

@ -39,13 +39,16 @@ const { openSidePanels, closePanel } = usePanels();
const resolvedComponents = shallowRef<Record<string, Component>>({}); const resolvedComponents = shallowRef<Record<string, Component>>({});
watch(openSidePanels, (panels) => { watch(openSidePanels, (panels) => {
const current = { ...resolvedComponents.value }; const current = resolvedComponents.value;
let changed = false;
const updates = { ...current };
for (const panel of panels) { for (const panel of panels) {
if (!current[panel.id]) { if (!updates[panel.id]) {
current[panel.id] = defineAsyncComponent(panel.component); updates[panel.id] = defineAsyncComponent(panel.component);
changed = true;
} }
} }
resolvedComponents.value = current; if (changed) resolvedComponents.value = updates;
}, { immediate: true }); }, { immediate: true });
function panelProps(id: PanelId): Record<string, any> { function panelProps(id: PanelId): Record<string, any> {

View File

@ -83,11 +83,12 @@ const agentsRoute = useRoute();
const { isChatOpen, isWorkspaceOpen, hasDebugColumn } = usePanels(); const { isChatOpen, isWorkspaceOpen, hasDebugColumn } = usePanels();
// Auth + agents // Auth + agents
const { connected, send: wsSend, switchAgent: wsSwitchAgent } = ws; const { connected, send: wsSend } = ws;
const { isLoggedIn } = auth; const { isLoggedIn } = auth;
const { selectedAgent, selectedMode, filteredAgents, defaultAgent, allAgents } = agents; const { selectedAgent, selectedMode, filteredAgents, defaultAgent, allAgents } = agents;
// Agent picker // Agent picker disabled for now (auto-select default agent)
// TODO: re-enable when multi-agent UX is designed
const showPicker = computed(() => false); const showPicker = computed(() => false);
const SEGMENTS = ['personal', 'common', 'private', 'public'] as const; const SEGMENTS = ['personal', 'common', 'private', 'public'] as const;
const agentSegments = computed(() => const agentSegments = computed(() =>