Output (SCN)
{scnOutput || (isLoading ? "Generating..." : "Output will appear here.")}
import { useEffect, useCallback, useMemo, useState } from 'react'; import { generateScn, initializeTokenizer, countTokens } from 'scn-ts-core'; import { Button } from './components/ui/button'; import { Textarea } from './components/ui/textarea'; import LogViewer from './components/LogViewer'; import { OutputOptions } from './components/OutputOptions'; import { Legend } from './components/Legend'; import { Play, Loader, Copy, Check, StopCircle, ZoomIn, ZoomOut, RefreshCw } from 'lucide-react'; import { Accordion, AccordionContent, AccordionItem, AccordionHeader, AccordionTrigger } from './components/ui/accordion'; import { useAnalysis } from './hooks/useAnalysis.hook'; import { useClipboard } from './hooks/useClipboard.hook'; import { useResizableSidebar } from './hooks/useResizableSidebar.hook'; import { useAppStore } from './stores/app.store'; import { cn } from './lib/utils'; import type { CodeSymbol } from 'scn-ts-core'; function App() { const { filesInput, setFilesInput, scnOutput, setScnOutput, formattingOptions, setFormattingOptions, includePattern, setIncludePattern, excludePattern, setExcludePattern, } = useAppStore(); const { isInitialized, isLoading, analysisResult, progress, logs, analysisTime, tokenImpact, handleAnalyze: performAnalysis, handleStop, onLogPartial, } = useAnalysis(); const [zoomLevel, setZoomLevel] = useState(1); const baseFontSizeRem = 0.75; // Corresponds to text-xs const handleZoomIn = () => setZoomLevel(z => Math.min(z * 1.2, 4)); const handleZoomOut = () => setZoomLevel(z => Math.max(z / 1.2, 0.25)); const handleZoomReset = () => setZoomLevel(1); const { sidebarWidth, handleMouseDown } = useResizableSidebar(480); const { isCopied, handleCopy: performCopy } = useClipboard(); useEffect(() => { if (!initializeTokenizer()) { onLogPartial({ level: 'error', message: 'Failed to initialize tokenizer.' }); } }, [onLogPartial]); useEffect(() => { if (analysisResult) { setScnOutput(generateScn(analysisResult, formattingOptions)); } else { setScnOutput(''); } }, [analysisResult, formattingOptions]); const { tokenCounts, tokenReductionPercent } = useMemo(() => { const input = countTokens(filesInput); const output = countTokens(scnOutput); let reductionPercent: number | null = null; if (input > 0) { reductionPercent = ((input - output) / input) * 100; } return { tokenCounts: { input, output }, tokenReductionPercent: reductionPercent, }; }, [filesInput, scnOutput]); const handleCopy = useCallback(() => { performCopy(scnOutput); }, [performCopy, scnOutput]); const handleAnalyze = useCallback(async () => { performAnalysis(filesInput, formattingOptions, includePattern, excludePattern); }, [performAnalysis, filesInput, formattingOptions, includePattern, excludePattern]); const { totalSymbols, visibleSymbols } = useMemo(() => { if (!analysisResult) { return { totalSymbols: 0, visibleSymbols: 0 }; } const allSymbols: CodeSymbol[] = analysisResult.flatMap(file => file.symbols); const total = allSymbols.length; let visibleSymbolsArr = allSymbols; if (formattingOptions.showOnlyExports) { visibleSymbolsArr = visibleSymbolsArr.filter(symbol => symbol.isExported); } if (formattingOptions.displayFilters) { const filters = formattingOptions.displayFilters; visibleSymbolsArr = visibleSymbolsArr.filter(symbol => { return (filters[symbol.kind] ?? filters['*'] ?? true); }); } return { totalSymbols: total, visibleSymbols: visibleSymbolsArr.length }; }, [analysisResult, formattingOptions.displayFilters, formattingOptions.showOnlyExports]); return (
{scnOutput || (isLoading ? "Generating..." : "Output will appear here.")}