| banksean | 659b983 | 2025-06-27 00:50:41 +0000 | [diff] [blame] | 1 | /** |
| 2 | * Demo module for sketch-call-status component |
| 3 | */ |
| 4 | |
| 5 | import { DemoModule } from "./demo-framework/types"; |
| 6 | import { |
| 7 | demoUtils, |
| 8 | idleCallStatus, |
| 9 | workingCallStatus, |
| 10 | heavyWorkingCallStatus, |
| 11 | disconnectedCallStatus, |
| 12 | workingDisconnectedCallStatus, |
| 13 | } from "./demo-fixtures/index"; |
| 14 | import type { CallStatusState } from "./demo-fixtures/index"; |
| 15 | |
| 16 | const demo: DemoModule = { |
| 17 | title: "Call Status Demo", |
| 18 | description: |
| 19 | "Display current LLM and tool call status with visual indicators", |
| 20 | imports: ["../sketch-call-status"], |
| 21 | styles: ["/dist/tailwind.css"], |
| 22 | |
| 23 | setup: async (container: HTMLElement) => { |
| 24 | // Create demo sections |
| 25 | const statusVariationsSection = demoUtils.createDemoSection( |
| 26 | "Status Variations", |
| 27 | "Different states of the call status component", |
| 28 | ); |
| 29 | |
| 30 | const interactiveSection = demoUtils.createDemoSection( |
| 31 | "Interactive Demo", |
| 32 | "Dynamically change call status to see real-time updates", |
| 33 | ); |
| 34 | |
| 35 | // Helper function to create status component with state |
| 36 | const createStatusComponent = ( |
| 37 | id: string, |
| 38 | state: CallStatusState, |
| 39 | label: string, |
| 40 | ) => { |
| 41 | const wrapper = document.createElement("div"); |
| banksean | 3d1308e | 2025-07-29 17:20:10 +0000 | [diff] [blame] | 42 | wrapper.className = |
| 43 | "my-4 p-3 border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800"; |
| banksean | 659b983 | 2025-06-27 00:50:41 +0000 | [diff] [blame] | 44 | |
| 45 | const labelEl = document.createElement("h4"); |
| 46 | labelEl.textContent = label; |
| 47 | labelEl.style.cssText = |
| banksean | 1ee0bc6 | 2025-07-22 23:24:18 +0000 | [diff] [blame] | 48 | "margin: 0 0 10px 0; color: var(--demo-label-color); font-size: 14px; font-weight: 600;"; |
| banksean | 659b983 | 2025-06-27 00:50:41 +0000 | [diff] [blame] | 49 | |
| 50 | const statusComponent = document.createElement( |
| 51 | "sketch-call-status", |
| 52 | ) as any; |
| 53 | statusComponent.id = id; |
| 54 | statusComponent.llmCalls = state.llmCalls; |
| 55 | statusComponent.toolCalls = state.toolCalls; |
| 56 | statusComponent.agentState = state.agentState; |
| 57 | statusComponent.isIdle = state.isIdle; |
| 58 | statusComponent.isDisconnected = state.isDisconnected; |
| 59 | |
| 60 | wrapper.appendChild(labelEl); |
| 61 | wrapper.appendChild(statusComponent); |
| 62 | return wrapper; |
| 63 | }; |
| 64 | |
| 65 | // Create status variations |
| 66 | const idleStatus = createStatusComponent( |
| 67 | "idle-status", |
| 68 | idleCallStatus, |
| 69 | "Idle State - No active calls", |
| 70 | ); |
| 71 | |
| 72 | const workingStatus = createStatusComponent( |
| 73 | "working-status", |
| 74 | workingCallStatus, |
| 75 | "Working State - LLM and tool calls active", |
| 76 | ); |
| 77 | |
| 78 | const heavyWorkingStatus = createStatusComponent( |
| 79 | "heavy-working-status", |
| 80 | heavyWorkingCallStatus, |
| 81 | "Heavy Working State - Multiple calls active", |
| 82 | ); |
| 83 | |
| 84 | const disconnectedStatus = createStatusComponent( |
| 85 | "disconnected-status", |
| 86 | disconnectedCallStatus, |
| 87 | "Disconnected State - No connection", |
| 88 | ); |
| 89 | |
| 90 | const workingDisconnectedStatus = createStatusComponent( |
| 91 | "working-disconnected-status", |
| 92 | workingDisconnectedCallStatus, |
| 93 | "Working but Disconnected - Calls active but no connection", |
| 94 | ); |
| 95 | |
| 96 | // Interactive demo component |
| 97 | const interactiveStatus = document.createElement( |
| 98 | "sketch-call-status", |
| 99 | ) as any; |
| 100 | interactiveStatus.id = "interactive-status"; |
| 101 | interactiveStatus.llmCalls = 0; |
| 102 | interactiveStatus.toolCalls = []; |
| 103 | interactiveStatus.agentState = null; |
| 104 | interactiveStatus.isIdle = true; |
| 105 | interactiveStatus.isDisconnected = false; |
| 106 | |
| 107 | // Control buttons for interactive demo |
| 108 | const controlsDiv = document.createElement("div"); |
| 109 | controlsDiv.style.cssText = |
| 110 | "margin-top: 20px; display: flex; flex-wrap: wrap; gap: 10px;"; |
| 111 | |
| 112 | const addLLMCallButton = demoUtils.createButton("Add LLM Call", () => { |
| 113 | interactiveStatus.llmCalls = interactiveStatus.llmCalls + 1; |
| 114 | interactiveStatus.isIdle = false; |
| 115 | }); |
| 116 | |
| 117 | const removeLLMCallButton = demoUtils.createButton( |
| 118 | "Remove LLM Call", |
| 119 | () => { |
| 120 | interactiveStatus.llmCalls = Math.max( |
| 121 | 0, |
| 122 | interactiveStatus.llmCalls - 1, |
| 123 | ); |
| 124 | if ( |
| 125 | interactiveStatus.llmCalls === 0 && |
| 126 | interactiveStatus.toolCalls.length === 0 |
| 127 | ) { |
| 128 | interactiveStatus.isIdle = true; |
| 129 | } |
| 130 | }, |
| 131 | ); |
| 132 | |
| 133 | const addToolCallButton = demoUtils.createButton("Add Tool Call", () => { |
| 134 | const toolNames = [ |
| 135 | "bash", |
| 136 | "patch", |
| 137 | "think", |
| 138 | "keyword_search", |
| 139 | "browser_navigate", |
| 140 | "codereview", |
| 141 | ]; |
| 142 | const randomTool = |
| 143 | toolNames[Math.floor(Math.random() * toolNames.length)]; |
| 144 | const currentTools = Array.isArray(interactiveStatus.toolCalls) |
| 145 | ? [...interactiveStatus.toolCalls] |
| 146 | : []; |
| 147 | if (!currentTools.includes(randomTool)) { |
| 148 | currentTools.push(randomTool); |
| 149 | interactiveStatus.toolCalls = currentTools; |
| 150 | interactiveStatus.isIdle = false; |
| 151 | } |
| 152 | }); |
| 153 | |
| 154 | const removeToolCallButton = demoUtils.createButton( |
| 155 | "Remove Tool Call", |
| 156 | () => { |
| 157 | const currentTools = Array.isArray(interactiveStatus.toolCalls) |
| 158 | ? [...interactiveStatus.toolCalls] |
| 159 | : []; |
| 160 | if (currentTools.length > 0) { |
| 161 | currentTools.pop(); |
| 162 | interactiveStatus.toolCalls = currentTools; |
| 163 | if (interactiveStatus.llmCalls === 0 && currentTools.length === 0) { |
| 164 | interactiveStatus.isIdle = true; |
| 165 | } |
| 166 | } |
| 167 | }, |
| 168 | ); |
| 169 | |
| 170 | const toggleConnectionButton = demoUtils.createButton( |
| 171 | "Toggle Connection", |
| 172 | () => { |
| 173 | interactiveStatus.isDisconnected = !interactiveStatus.isDisconnected; |
| 174 | }, |
| 175 | ); |
| 176 | |
| 177 | const setAgentStateButton = demoUtils.createButton( |
| 178 | "Change Agent State", |
| 179 | () => { |
| 180 | const states = [ |
| 181 | null, |
| 182 | "analyzing code", |
| 183 | "refactoring components", |
| 184 | "running tests", |
| 185 | "reviewing changes", |
| 186 | "generating documentation", |
| 187 | ]; |
| 188 | const currentIndex = states.indexOf(interactiveStatus.agentState); |
| 189 | const nextIndex = (currentIndex + 1) % states.length; |
| 190 | interactiveStatus.agentState = states[nextIndex]; |
| 191 | }, |
| 192 | ); |
| 193 | |
| 194 | const resetButton = demoUtils.createButton("Reset to Idle", () => { |
| 195 | interactiveStatus.llmCalls = 0; |
| 196 | interactiveStatus.toolCalls = []; |
| 197 | interactiveStatus.agentState = null; |
| 198 | interactiveStatus.isIdle = true; |
| 199 | interactiveStatus.isDisconnected = false; |
| 200 | }); |
| 201 | |
| 202 | controlsDiv.appendChild(addLLMCallButton); |
| 203 | controlsDiv.appendChild(removeLLMCallButton); |
| 204 | controlsDiv.appendChild(addToolCallButton); |
| 205 | controlsDiv.appendChild(removeToolCallButton); |
| 206 | controlsDiv.appendChild(toggleConnectionButton); |
| 207 | controlsDiv.appendChild(setAgentStateButton); |
| 208 | controlsDiv.appendChild(resetButton); |
| 209 | |
| 210 | // Assemble the demo |
| 211 | statusVariationsSection.appendChild(idleStatus); |
| 212 | statusVariationsSection.appendChild(workingStatus); |
| 213 | statusVariationsSection.appendChild(heavyWorkingStatus); |
| 214 | statusVariationsSection.appendChild(disconnectedStatus); |
| 215 | statusVariationsSection.appendChild(workingDisconnectedStatus); |
| 216 | |
| 217 | const interactiveWrapper = document.createElement("div"); |
| banksean | 3d1308e | 2025-07-29 17:20:10 +0000 | [diff] [blame] | 218 | interactiveWrapper.className = |
| 219 | "p-3 border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800"; |
| banksean | 659b983 | 2025-06-27 00:50:41 +0000 | [diff] [blame] | 220 | interactiveWrapper.appendChild(interactiveStatus); |
| 221 | interactiveWrapper.appendChild(controlsDiv); |
| 222 | interactiveSection.appendChild(interactiveWrapper); |
| 223 | |
| 224 | container.appendChild(statusVariationsSection); |
| 225 | container.appendChild(interactiveSection); |
| 226 | |
| 227 | // Add some simulation of real activity |
| 228 | const simulationInterval = setInterval(() => { |
| 229 | const statusComponents = [ |
| 230 | document.getElementById("working-status") as any, |
| 231 | document.getElementById("heavy-working-status") as any, |
| 232 | ].filter(Boolean); |
| 233 | |
| 234 | statusComponents.forEach((statusEl) => { |
| 235 | if (statusEl && Math.random() > 0.8) { |
| 236 | // 20% chance to update |
| 237 | // Simulate some activity by slightly changing the number of calls |
| 238 | const variation = Math.floor(Math.random() * 3) - 1; // -1, 0, or 1 |
| 239 | statusEl.llmCalls = Math.max(0, statusEl.llmCalls + variation); |
| 240 | } |
| 241 | }); |
| 242 | }, 2000); |
| 243 | |
| 244 | // Store interval for cleanup |
| 245 | (container as any).demoInterval = simulationInterval; |
| 246 | }, |
| 247 | |
| 248 | cleanup: async () => { |
| 249 | // Clear any intervals |
| 250 | const container = document.getElementById("demo-container"); |
| 251 | if (container && (container as any).demoInterval) { |
| 252 | clearInterval((container as any).demoInterval); |
| 253 | delete (container as any).demoInterval; |
| 254 | } |
| 255 | }, |
| 256 | }; |
| 257 | |
| 258 | export default demo; |