feat: Add multi-host config and dynamic project scanning
Backend: - Load hosts from config/hosts.json - New /api/hosts endpoint listing available hosts - Dynamic project scanning with configurable depth - Support for local and SSH hosts (SSH execution coming next) Frontend (by Web-UI Claude): - Slash commands: /clear, /help, /export, /scroll, /new, /info - Chat export as Markdown Config: - hosts.json defines hosts with connection info and base paths - hosts.example.json as template (real config is gitignored) - Each host has name, description, color, icon, basePaths Next steps: - SSH command execution for remote hosts - Frontend host selector UI - Multi-agent collaboration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,89 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import { useClaudeSession } from './hooks/useClaudeSession';
|
||||
import { MessageList } from './components/MessageList';
|
||||
import { ChatInput } from './components/ChatInput';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
import { Header } from './components/Header';
|
||||
|
||||
// Slash command definitions
|
||||
const SLASH_COMMANDS = {
|
||||
clear: {
|
||||
description: 'Clear chat history (UI only)',
|
||||
execute: ({ clearMessages, addSystemMessage }) => {
|
||||
clearMessages();
|
||||
addSystemMessage('Chat cleared');
|
||||
}
|
||||
},
|
||||
help: {
|
||||
description: 'Show available commands',
|
||||
execute: ({ addSystemMessage }) => {
|
||||
const helpText = Object.entries(SLASH_COMMANDS)
|
||||
.map(([cmd, { description }]) => `/${cmd} - ${description}`)
|
||||
.join('\n');
|
||||
addSystemMessage(`Available commands:\n${helpText}`);
|
||||
}
|
||||
},
|
||||
export: {
|
||||
description: 'Export chat as Markdown',
|
||||
execute: ({ messages, addSystemMessage }) => {
|
||||
const markdown = messages.map(m => {
|
||||
if (m.type === 'user') return `**You:** ${m.content}`;
|
||||
if (m.type === 'assistant') return `**Claude:** ${m.content}`;
|
||||
if (m.type === 'tool_use') return `> Tool: ${m.tool}`;
|
||||
if (m.type === 'system') return `_${m.content}_`;
|
||||
return '';
|
||||
}).filter(Boolean).join('\n\n');
|
||||
|
||||
const blob = new Blob([markdown], { type: 'text/markdown' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `claude-chat-${new Date().toISOString().slice(0,10)}.md`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
addSystemMessage('Chat exported as Markdown');
|
||||
}
|
||||
},
|
||||
scroll: {
|
||||
description: 'Scroll to top or bottom (/scroll top|bottom)',
|
||||
execute: ({ args, addSystemMessage }) => {
|
||||
const direction = args[0] || 'bottom';
|
||||
const container = document.querySelector('.overflow-y-auto');
|
||||
if (container) {
|
||||
if (direction === 'top') {
|
||||
container.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
} else {
|
||||
container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
addSystemMessage(`Scrolled to ${direction}`);
|
||||
}
|
||||
},
|
||||
new: {
|
||||
description: 'Start a new session (clears history)',
|
||||
execute: ({ clearMessages, stopSession, startSession, selectedProject, addSystemMessage }) => {
|
||||
stopSession();
|
||||
clearMessages();
|
||||
setTimeout(() => {
|
||||
startSession(selectedProject, false); // false = don't resume
|
||||
}, 500);
|
||||
addSystemMessage('Starting new session...');
|
||||
}
|
||||
},
|
||||
info: {
|
||||
description: 'Show session info',
|
||||
execute: ({ connected, sessionActive, currentProject, messages, addSystemMessage }) => {
|
||||
const info = [
|
||||
`Connected: ${connected ? 'Yes' : 'No'}`,
|
||||
`Session: ${sessionActive ? 'Active' : 'Inactive'}`,
|
||||
`Project: ${currentProject || 'None'}`,
|
||||
`Messages: ${messages.length}`
|
||||
].join('\n');
|
||||
addSystemMessage(info);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function App() {
|
||||
const {
|
||||
connected,
|
||||
@@ -17,21 +96,67 @@ function App() {
|
||||
sendMessage,
|
||||
stopSession,
|
||||
clearMessages,
|
||||
setError
|
||||
setError,
|
||||
setMessages
|
||||
} = useClaudeSession();
|
||||
|
||||
const [selectedProject, setSelectedProject] = useState('/projects/claude-web-ui');
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const [resumeSession, setResumeSession] = useState(true);
|
||||
|
||||
// Add system message helper
|
||||
const addSystemMessage = useCallback((content) => {
|
||||
setMessages(prev => [...prev, {
|
||||
type: 'system',
|
||||
content,
|
||||
timestamp: Date.now()
|
||||
}]);
|
||||
}, [setMessages]);
|
||||
|
||||
const handleStartSession = () => {
|
||||
startSession(selectedProject, resumeSession);
|
||||
};
|
||||
|
||||
const handleSendMessage = (message) => {
|
||||
if (message.trim()) {
|
||||
sendMessage(message);
|
||||
// Handle slash commands
|
||||
const handleCommand = useCallback((command, args) => {
|
||||
const cmd = SLASH_COMMANDS[command.toLowerCase()];
|
||||
if (cmd) {
|
||||
cmd.execute({
|
||||
clearMessages,
|
||||
addSystemMessage,
|
||||
messages,
|
||||
stopSession,
|
||||
startSession,
|
||||
selectedProject,
|
||||
connected,
|
||||
sessionActive,
|
||||
currentProject,
|
||||
args
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, [clearMessages, addSystemMessage, messages, stopSession, startSession, selectedProject, connected, sessionActive, currentProject]);
|
||||
|
||||
const handleSendMessage = (message) => {
|
||||
if (!message.trim()) return;
|
||||
|
||||
// Check for slash command
|
||||
if (message.startsWith('/')) {
|
||||
const parts = message.slice(1).split(' ');
|
||||
const command = parts[0];
|
||||
const args = parts.slice(1);
|
||||
|
||||
if (handleCommand(command, args)) {
|
||||
return; // Command handled
|
||||
} else {
|
||||
addSystemMessage(`Unknown command: /${command}. Type /help for available commands.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Regular message
|
||||
sendMessage(message);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user