From fa1fe8613acfe30f794d3a2f10a594615850d18d Mon Sep 17 00:00:00 2001 From: teernisse Date: Sat, 28 Feb 2026 00:47:49 -0500 Subject: [PATCH] feat(dashboard): wire skills autocomplete configuration to session cards Connect the skills enumeration API to session card input fields for slash command autocomplete: App.js: - Add skillsConfig state for Claude and Codex skill configs - Fetch skills for both agent types on mount using Promise.all - Pass agent-appropriate autocompleteConfig to each SessionCard SessionCard.js: - Accept autocompleteConfig prop and forward to SimpleInput - Move context usage display from header to footer status bar for better information hierarchy (activity indicator + context together) SimpleInput.js: - Fix autocomplete dropdown padding (py-2 -> py-1.5) - Fix font inheritance (add font-mono to skill name) - Fix description tooltip whitespace handling (add font-sans, whitespace-normal) SpawnModal.js: - Add SPAWN_TIMEOUT_MS (2x default) to handle pending spawn registry wait time plus session file confirmation polling AgentActivityIndicator.js: - Minor styling refinement for status display Co-Authored-By: Claude Opus 4.5 --- .../components/AgentActivityIndicator.js | 2 +- dashboard/components/App.js | 17 +++++++++++++- dashboard/components/SessionCard.js | 22 +++++++++++-------- dashboard/components/SimpleInput.js | 12 +++++----- dashboard/components/SpawnModal.js | 8 +++++-- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/dashboard/components/AgentActivityIndicator.js b/dashboard/components/AgentActivityIndicator.js index aac37e9..c6727a4 100644 --- a/dashboard/components/AgentActivityIndicator.js +++ b/dashboard/components/AgentActivityIndicator.js @@ -92,7 +92,7 @@ export function AgentActivityIndicator({ session }) { const tokenDisplay = formatTokens(turnTokens); return html` -
+
${isActive && html` `} diff --git a/dashboard/components/App.js b/dashboard/components/App.js index 14ac725..f014c82 100644 --- a/dashboard/components/App.js +++ b/dashboard/components/App.js @@ -1,5 +1,5 @@ import { html, useState, useEffect, useCallback, useMemo, useRef } from '../lib/preact.js'; -import { API_STATE, API_STREAM, API_DISMISS, API_DISMISS_DEAD, API_RESPOND, API_CONVERSATION, API_HEALTH, POLL_MS, fetchWithTimeout } from '../utils/api.js'; +import { API_STATE, API_STREAM, API_DISMISS, API_DISMISS_DEAD, API_RESPOND, API_CONVERSATION, API_HEALTH, POLL_MS, fetchWithTimeout, fetchSkills } from '../utils/api.js'; import { groupSessionsByProject } from '../utils/status.js'; import { Sidebar } from './Sidebar.js'; import { SessionCard } from './SessionCard.js'; @@ -23,6 +23,7 @@ export function App() { const [zellijAvailable, setZellijAvailable] = useState(true); const [newlySpawnedIds, setNewlySpawnedIds] = useState(new Set()); const pendingSpawnIdsRef = useRef(new Set()); + const [skillsConfig, setSkillsConfig] = useState({ claude: null, codex: null }); // Background conversation refresh with error tracking const refreshConversationSilent = useCallback(async (sessionId, projectDir, agent = 'claude') => { @@ -353,6 +354,18 @@ export function App() { return () => clearInterval(interval); }, []); + // Fetch skills for autocomplete on mount + useEffect(() => { + const loadSkills = async () => { + const [claude, codex] = await Promise.all([ + fetchSkills('claude'), + fetchSkills('codex') + ]); + setSkillsConfig({ claude, codex }); + }; + loadSkills(); + }, []); + // Group sessions by project const projectGroups = groupSessionsByProject(sessions); @@ -524,6 +537,7 @@ export function App() { onRespond=${respondToSession} onDismiss=${dismissSession} isNewlySpawned=${newlySpawnedIds.has(session.session_id)} + autocompleteConfig=${skillsConfig[session.agent === 'codex' ? 'codex' : 'claude']} /> `)}
@@ -577,6 +591,7 @@ export function App() { onFetchConversation=${fetchConversation} onRespond=${respondToSession} onDismiss=${dismissSession} + autocompleteConfig=${skillsConfig[session.agent === 'codex' ? 'codex' : 'claude']} /> `)}
diff --git a/dashboard/components/SessionCard.js b/dashboard/components/SessionCard.js index f9e1d09..a6ea69d 100644 --- a/dashboard/components/SessionCard.js +++ b/dashboard/components/SessionCard.js @@ -116,13 +116,6 @@ export function SessionCard({ session, onClick, conversation, onFetchConversatio `} - ${contextUsage && html` -
- ${contextUsage.headline} - ${contextUsage.detail} - ${contextUsage.trail && html`${contextUsage.trail}`} -
- `}
${formatDuration(session.started_at)} @@ -146,9 +139,20 @@ export function SessionCard({ session, onClick, conversation, onFetchConversatio <${ChatMessages} messages=${conversation || []} status=${session.status} limit=${enlarged ? null : 20} />
- +
- <${AgentActivityIndicator} session=${session} /> + +
+ <${AgentActivityIndicator} session=${session} /> + ${contextUsage && html` +
+ ${contextUsage.headline} + ${contextUsage.detail} + ${contextUsage.trail && html`${contextUsage.trail}`} +
+ `} +
+
${hasQuestions ? html` <${QuestionBlock} diff --git a/dashboard/components/SimpleInput.js b/dashboard/components/SimpleInput.js index 9acf746..f833d04 100644 --- a/dashboard/components/SimpleInput.js +++ b/dashboard/components/SimpleInput.js @@ -195,7 +195,7 @@ export function SimpleInput({ sessionId, status, onRespond, autocompleteConfig = ` : filteredSkills.map((skill, i) => html`
insertSkill(skill)} onMouseEnter=${() => setSelectedIndex(i)} > -
- ${autocompleteConfig.trigger}${skill.name} -
-
${skill.description}
+ ${autocompleteConfig.trigger}${skill.name} + ${i === selectedIndex && skill.description && html` +
+ ${skill.description} +
+ `}
`)}
diff --git a/dashboard/components/SpawnModal.js b/dashboard/components/SpawnModal.js index bbb2ce6..58f8e8e 100644 --- a/dashboard/components/SpawnModal.js +++ b/dashboard/components/SpawnModal.js @@ -1,5 +1,9 @@ import { html, useState, useEffect, useCallback, useRef } from '../lib/preact.js'; -import { API_PROJECTS, API_SPAWN, fetchWithTimeout } from '../utils/api.js'; +import { API_PROJECTS, API_SPAWN, fetchWithTimeout, API_TIMEOUT_MS } from '../utils/api.js'; + +// Spawn needs longer timeout: pending spawn registry requires discovery cycle to run, +// plus server polls for session file confirmation +const SPAWN_TIMEOUT_MS = API_TIMEOUT_MS * 2; export function SpawnModal({ isOpen, onClose, onSpawn, currentProject }) { const [projects, setProjects] = useState([]); @@ -95,7 +99,7 @@ export function SpawnModal({ isOpen, onClose, onSpawn, currentProject }) { 'Authorization': `Bearer ${window.AMC_AUTH_TOKEN}`, }, body: JSON.stringify({ project, agent_type: agentType }), - }); + }, SPAWN_TIMEOUT_MS); const data = await response.json(); if (data.ok) {