From 4c628a25a2fb836501d08279aa0c38d2ac162cd3 Mon Sep 17 00:00:00 2001 From: Rob Colbert Date: Fri, 15 May 2026 15:52:14 -0400 Subject: [PATCH] ChatSessionView refactor; and a scripts directory --- .gitignore | 1 + .../frontend/src/components/ProjectPanel.tsx | 43 +++ .../frontend/src/components/SessionPanel.tsx | 267 ++++++++++++++++++ .../frontend/src/pages/ChatSessionView.tsx | 266 +++-------------- scripts/forever-sleep.js | 27 ++ 5 files changed, 379 insertions(+), 225 deletions(-) create mode 100644 gadget-code/frontend/src/components/ProjectPanel.tsx create mode 100644 gadget-code/frontend/src/components/SessionPanel.tsx create mode 100644 scripts/forever-sleep.js diff --git a/.gitignore b/.gitignore index 3c7950e..f6cb181 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ gadget-drone.*.log logfetch .gadget +gadget/ node_modules diff --git a/gadget-code/frontend/src/components/ProjectPanel.tsx b/gadget-code/frontend/src/components/ProjectPanel.tsx new file mode 100644 index 0000000..ed5e1fb --- /dev/null +++ b/gadget-code/frontend/src/components/ProjectPanel.tsx @@ -0,0 +1,43 @@ +import type { Project } from '../lib/api'; + +interface ProjectPanelProps { + project: Project | null; + onNavigateToProject: () => void; +} + +export default function ProjectPanel({ project, onNavigateToProject }: ProjectPanelProps) { + return ( +
+
+

+ Project +

+ +
+
+ {project ? ( + <> +
+
Name
+
{project.name}
+
+
+
Slug
+
{project.slug}
+
+ + ) : ( +
Loading...
+ )} +
+
+ ); +} diff --git a/gadget-code/frontend/src/components/SessionPanel.tsx b/gadget-code/frontend/src/components/SessionPanel.tsx new file mode 100644 index 0000000..9a78eb3 --- /dev/null +++ b/gadget-code/frontend/src/components/SessionPanel.tsx @@ -0,0 +1,267 @@ +import { ChatSessionMode, type AiProvider, type ChatSession } from '../lib/api'; +import { WorkspaceMode } from '../lib/types'; +import WorkspaceModeIndicator from './WorkspaceModeIndicator'; + +interface SessionPanelProps { + session: ChatSession | null; + workspaceMode: WorkspaceMode; + sessionLocked: boolean; + providers: AiProvider[]; + selectedProviderId: string; + selectedModelId: string; + sessionReasoningEffort: string; + isEditingProvider: boolean; + editProviderId: string; + editModelId: string; + isUpdatingProvider: boolean; + isEditingName: boolean; + editName: string; + isUpdatingName: boolean; + isUpdatingMode: boolean; + isUpdatingReasoning: boolean; + onWorkspaceModeChange: (mode: WorkspaceMode) => void; + onModeChange: (e: React.ChangeEvent) => void; + onReasoningChange: (e: React.ChangeEvent) => void; + onStartEditProvider: () => void; + onSaveProvider: () => void; + onCancelEditProvider: () => void; + onEditProviderIdChange: (id: string) => void; + onEditModelIdChange: (id: string) => void; + onStartEditName: () => void; + onSaveName: () => void; + onCancelEditName: () => void; + onEditNameChange: (name: string) => void; + getProviderName: (id: string) => string; + getModelName: (providerId: string, modelId: string) => string; + getSortedModels: (providerId: string) => AiProvider['models']; + truncateModelName: (name: string, maxLength?: number) => string; + getSelectedModelCapabilities: () => { hasThinking: boolean } | null; +} + +export default function SessionPanel({ + session, + workspaceMode, + sessionLocked, + providers, + selectedProviderId, + selectedModelId, + sessionReasoningEffort, + isEditingProvider, + editProviderId, + editModelId, + isUpdatingProvider, + isEditingName, + editName, + isUpdatingName, + isUpdatingMode, + isUpdatingReasoning, + onWorkspaceModeChange, + onModeChange, + onReasoningChange, + onStartEditProvider, + onSaveProvider, + onCancelEditProvider, + onEditProviderIdChange, + onEditModelIdChange, + onStartEditName, + onSaveName, + onCancelEditName, + onEditNameChange, + getProviderName, + getModelName, + getSortedModels, + truncateModelName, + getSelectedModelCapabilities, +}: SessionPanelProps) { + return ( +
+
+

+ Session +

+ +
+
+
+
Name
+ {isEditingName ? ( +
+ onEditNameChange(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { e.preventDefault(); onSaveName(); } + if (e.key === 'Escape') { e.preventDefault(); onCancelEditName(); } + }} + className="flex-1 px-2 py-1.5 bg-bg-tertiary border border-border-default rounded text-text-primary text-sm focus:border-brand focus:outline-none" + autoFocus + /> + + +
+ ) : ( +
+
{session?.name}
+ +
+ )} +
+ {isEditingProvider ? ( +
+
+
Provider
+ +
+
+
Model
+ +
+
+ + +
+
+ ) : ( +
+
+
Provider
+
{getProviderName(selectedProviderId)}
+
+
+
Model
+
+ {getModelName(selectedProviderId, selectedModelId)} +
+
+ +
+ )} +
+
Mode
+ +
+
+
Reasoning
+ +
+
+
+ ); +} diff --git a/gadget-code/frontend/src/pages/ChatSessionView.tsx b/gadget-code/frontend/src/pages/ChatSessionView.tsx index 252c84c..bfe6b7c 100644 --- a/gadget-code/frontend/src/pages/ChatSessionView.tsx +++ b/gadget-code/frontend/src/pages/ChatSessionView.tsx @@ -3,7 +3,8 @@ import { useParams, useNavigate } from 'react-router-dom'; import { socketClient } from '../lib/socket'; import { chatSessionApi, projectApi, providerApi, type ChatSession, type ChatTurn, type ChatTurnBlock, ChatTurnStats, ChatSessionMode, type AiProvider, type Project } from '../lib/api'; import { WorkspaceMode } from '../lib/types'; -import WorkspaceModeIndicator from '../components/WorkspaceModeIndicator'; +import SessionPanel from '../components/SessionPanel'; +import ProjectPanel from '../components/ProjectPanel'; import FilesPanel from '../components/FilesPanel'; import LogPanel from '../components/LogPanel'; import ChatTurnComponent from '../components/ChatTurn'; @@ -1164,232 +1165,47 @@ export default function ChatSessionView() { {/* Sidebar */}
- {/* SESSION Panel */} -
-
-

- Session -

- -
-
-
-
Name
- {isEditingName ? ( -
- setEditName(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter') { e.preventDefault(); handleSaveName(); } - if (e.key === 'Escape') { e.preventDefault(); handleCancelEditName(); } - }} - className="flex-1 px-2 py-1.5 bg-bg-tertiary border border-border-default rounded text-text-primary text-sm focus:border-brand focus:outline-none" - autoFocus - /> - - -
- ) : ( -
-
{session?.name}
- -
- )} -
- {isEditingProvider ? ( -
-
-
Provider
- -
-
-
Model
- -
-
- - -
-
- ) : ( -
-
-
Provider
-
{getProviderName(selectedProviderId)}
-
-
-
Model
-
- {getModelName(selectedProviderId, selectedModelId)} -
-
- -
- )} -
-
Mode
- -
-
-
Reasoning
- -
-
-
+ - {/* PROJECT Panel */} -
-
-

- Project -

- -
-
- {project ? ( - <> -
-
Name
-
{project.name}
-
-
-
Slug
-
{project.slug}
-
- - ) : ( -
Loading...
- )} -
-
+ navigate(`/projects/${project?.slug}`)} + /> - {/* FILES Panel */} { diff --git a/scripts/forever-sleep.js b/scripts/forever-sleep.js new file mode 100644 index 0000000..4e4d694 --- /dev/null +++ b/scripts/forever-sleep.js @@ -0,0 +1,27 @@ +#!/usr/bin/env node + +/** + * forever-sleep.js + * A script that sleeps, wakes up every 5 seconds, logs to stdout, then goes back to sleep. + */ + +const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); + +async function main() { + console.log('forever-sleep: Starting...'); + + while (true) { + const timestamp = new Date().toISOString(); + console.log(`forever-sleep: ${timestamp} - Waking up (sleeping for 5 seconds)...`); + + await sleep(5000); + + const timestampAfter = new Date().toISOString(); + console.log(`forever-sleep: ${timestampAfter} - Back to sleep...`); + } +} + +main().catch(err => { + console.error('forever-sleep: Error:', err); + process.exit(1); +});