Backend: - Add CategoryMode enum with 4 modes (fixed_only, fixed_plus_custom, custom_only, dynamic_auto) - Add Step 0 for LLM category analysis before attribute generation - Implement dynamic prompts for Step 1/2 that work with N categories - Add execute_step0(), resolve_final_categories(), assemble_dynamic_attribute_tree() - Update SSE events to include step0_start, step0_complete, categories_resolved Frontend: - Add CategorySelector component with mode selection, custom category input, and category count slider - Update types with CategoryDefinition, Step0Result, DynamicStep1Result, DynamicCausalChain - Update api.ts with new SSE event handlers - Update useAttribute hook with category parameters - Integrate CategorySelector into InputPanel - Fix mindmap to dynamically extract and display N categories (was hardcoded to 4) - Add CSS styles for depth 5-8 to support more category levels 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
112 lines
3.2 KiB
TypeScript
112 lines
3.2 KiB
TypeScript
import { useState, useRef, useCallback } from 'react';
|
|
import { ConfigProvider, Layout, theme, Typography } from 'antd';
|
|
import { ThemeToggle } from './components/ThemeToggle';
|
|
import { InputPanel } from './components/InputPanel';
|
|
import { MindmapPanel } from './components/MindmapPanel';
|
|
import { useAttribute } from './hooks/useAttribute';
|
|
import type { MindmapD3Ref } from './components/MindmapD3';
|
|
import type { CategoryMode } from './types';
|
|
|
|
const { Header, Sider, Content } = Layout;
|
|
const { Title } = Typography;
|
|
|
|
interface VisualSettings {
|
|
nodeSpacing: number;
|
|
fontSize: number;
|
|
}
|
|
|
|
function App() {
|
|
const [isDark, setIsDark] = useState(true);
|
|
const { loading, progress, error, currentResult, history, analyze, loadFromHistory } = useAttribute();
|
|
const [visualSettings, setVisualSettings] = useState<VisualSettings>({
|
|
nodeSpacing: 32,
|
|
fontSize: 14,
|
|
});
|
|
const mindmapRef = useRef<MindmapD3Ref>(null);
|
|
|
|
const handleAnalyze = async (
|
|
query: string,
|
|
model?: string,
|
|
temperature?: number,
|
|
chainCount?: number,
|
|
categoryMode?: CategoryMode,
|
|
customCategories?: string[],
|
|
suggestedCategoryCount?: number
|
|
) => {
|
|
await analyze(query, model, temperature, chainCount, categoryMode, customCategories, suggestedCategoryCount);
|
|
};
|
|
|
|
const handleExpandAll = useCallback(() => {
|
|
mindmapRef.current?.expandAll();
|
|
}, []);
|
|
|
|
const handleCollapseAll = useCallback(() => {
|
|
mindmapRef.current?.collapseAll();
|
|
}, []);
|
|
|
|
return (
|
|
<ConfigProvider
|
|
theme={{
|
|
algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm,
|
|
}}
|
|
>
|
|
<Layout style={{ minHeight: '100vh' }}>
|
|
<Header
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
padding: '0 24px',
|
|
}}
|
|
>
|
|
<Title level={4} style={{ margin: 0, color: isDark ? '#fff' : '#000' }}>
|
|
Attribute Agent
|
|
</Title>
|
|
<ThemeToggle isDark={isDark} onToggle={setIsDark} />
|
|
</Header>
|
|
<Layout>
|
|
<Content
|
|
style={{
|
|
padding: 16,
|
|
height: 'calc(100vh - 64px)',
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
<MindmapPanel
|
|
ref={mindmapRef}
|
|
data={currentResult}
|
|
loading={loading}
|
|
error={error}
|
|
isDark={isDark}
|
|
visualSettings={visualSettings}
|
|
/>
|
|
</Content>
|
|
<Sider
|
|
width={350}
|
|
theme={isDark ? 'dark' : 'light'}
|
|
style={{
|
|
height: 'calc(100vh - 64px)',
|
|
overflow: 'auto',
|
|
}}
|
|
>
|
|
<InputPanel
|
|
loading={loading}
|
|
progress={progress}
|
|
history={history}
|
|
currentResult={currentResult}
|
|
onAnalyze={handleAnalyze}
|
|
onLoadHistory={loadFromHistory}
|
|
onExpandAll={handleExpandAll}
|
|
onCollapseAll={handleCollapseAll}
|
|
visualSettings={visualSettings}
|
|
onVisualSettingsChange={setVisualSettings}
|
|
/>
|
|
</Sider>
|
|
</Layout>
|
|
</Layout>
|
|
</ConfigProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|