Files
novelty-seeking/frontend/src/App.tsx
gbanyan 91f7f41bc1 feat: Add dynamic category system for attribute analysis
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>
2025-12-02 23:04:35 +08:00

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;