diff --git a/README.md b/README.md index 6950dc4..28a1596 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ Synapse is a Vite + React + TypeScript application that turns complex content in - **Rewriter API**: Text simplification with reading level control, tone adjustment (formal/casual), length modification, and streaming support - **Translator API**: Multi-language translation (20+ languages) with automatic language detection and format preservation - **Prompt API**: Conversational AI with temperature control, system prompts, and streaming responses +- **Writing Enhancement**: Comprehensive writing assistance combining multiple APIs: + - Grammar and spelling correction (Proofreader API) + - Tone adjustment (Writer + Rewriter APIs) + - Key point extraction for summaries (Prompt API) + - Text expansion and elaboration (Writer API) + - Actionable writing improvement suggestions (Prompt API) - **Service Layer**: Production-ready wrappers with error handling, retry logic, content validation, and sanitization ### UI Components @@ -44,11 +50,12 @@ Synapse is a Vite + React + TypeScript application that turns complex content in - **Status Indicators**: Visual feedback for API availability, processing state, and elapsed time tracking ### Developer Experience -- **Comprehensive Error Handling**: Type-safe error guards for Rewriter, Translator, and Prompt APIs -- **React Hooks**: `useRewriter`, `useTranslator`, `usePrompt`, `useTextInput`, `useFileUpload`, and `useContentExtraction` for state management +- **Comprehensive Error Handling**: Type-safe error guards for Rewriter, Translator, Prompt, Writer, and Proofreader APIs +- **React Hooks**: `useRewriter`, `useTranslator`, `usePrompt`, `useWritingEnhancement`, `useTextInput`, `useFileUpload`, and `useContentExtraction` for state management - **Browser Compatibility**: Automatic detection and user-friendly warnings for unsupported browsers - **Capability Detection**: Runtime checks for API availability with graceful degradation - **File Processing**: Secure local PDF.js worker, content validation, and robust extraction pipeline +- **Test Infrastructure**: Dedicated test pages for all Chrome AI APIs with real-time diagnostics and event logging ## Tooling & Architecture @@ -93,6 +100,7 @@ Access interactive demos at `/demos`: - **Rewriter API Demo** (`/demos/rewriter`): Test text simplification with various options - **Translator API Demo** (`/demos/translator`): Translate between 20+ languages with streaming - **Prompt API Demo** (`/demos/prompt`): Conversational AI with advanced configuration +- **Writing Enhancement Demo** (`/demos/writing-enhancement`): Test comprehensive writing assistance features Each demo includes: - Real-time API status indicators @@ -100,6 +108,7 @@ Each demo includes: - Error handling demonstrations - Browser compatibility checks - Comprehensive diagnostics +- Event logging for debugging ## Scripts @@ -213,12 +222,15 @@ clear() - **Chrome Canary 137+** (or Dev channel) - **Chrome AI flags enabled**: Navigate to `chrome://flags` and enable: - - `#optimization-guide-on-device-model` - - `#prompt-api-for-gemini-nano` - - `#translation-api` - - `#rewriter-api` + - `#optimization-guide-on-device-model` (required for model download) + - `#prompt-api-for-gemini-nano` (for Prompt API and writing suggestions) + - `#translation-api` (for Translator API) + - `#rewriter-api` (for Rewriter API and tone simplification) + - `#writer-api` (for Writer and Proofreader APIs - grammar correction, text expansion) - **AI model downloaded**: ~2GB download occurs automatically on first use +For detailed setup instructions with screenshots, see the **[Writing Enhancement Setup Guide](./docs/SETUP.md)**. + ## File Upload Configuration ### Security Features @@ -261,10 +273,45 @@ The URL extraction feature uses Mozilla Readability to extract clean article con - **Supported Sites**: Works best with article-based content (blogs, news sites, documentation) - **Content Sanitization**: All extracted HTML is sanitized with DOMPurify to prevent XSS attacks +### Writing Enhancement Service +```typescript +import { WritingEnhancementService } from '@/lib/chrome-ai/services' + +const service = new WritingEnhancementService() +await service.initialize() + +// Grammar and spelling correction +const corrected = await service.correctGrammar('Text with erors and mistakes') +console.log(corrected.stats) // { totalCorrections: 2, grammarErrors: 1, spellingErrors: 1, ... } + +// Adjust tone +const professional = await service.adjustTone('hey whats up!', 'professional') +const simple = await service.adjustTone('The algorithm utilizes...', 'simple') + +// Extract key points +const keyPoints = await service.extractKeyPoints('Long article text...') +console.log(keyPoints) // [{ point: "Main idea", importance: "primary" }, ...] + +// Get writing suggestions +const suggestions = await service.getWritingSuggestions('Text to improve') +console.log(suggestions) // [{ category: "clarity", issue: "...", suggestion: "...", priority: "high" }, ...] + +// Expand brief text +const expanded = await service.expandText('Brief intro', 'medium') + +// Run multiple enhancements at once +const enhanced = await service.enhanceText('Text to enhance', { + correctGrammar: true, + extractKeyPoints: true, + getSuggestions: true, + adjustTone: 'professional' +}) +``` + ## Next Steps - Expand Workspace page with multi-API processing pipelines - Add Summarizer and Language Detector API integrations -- Implement Writer & Proofreader API demos - Add automated tests (Vitest + React Testing Library) - Integrate state management (Zustand) for cross-component state +- Add quality scoring and telemetry for Writing Enhancement diff --git a/src/components/test/WritingEnhancementTest.tsx b/src/components/test/WritingEnhancementTest.tsx new file mode 100644 index 0000000..3b131ce --- /dev/null +++ b/src/components/test/WritingEnhancementTest.tsx @@ -0,0 +1,594 @@ +import { useState, useRef, useEffect, useCallback } from 'react' +import { WritingEnhancementService, type AIModelDownloadProgress } from '@/lib/chrome-ai' +import type { + WritingSuggestion, + KeyPoint, + WritingEnhancementResult, +} from '@/lib/chrome-ai/services/WritingEnhancementService' +import type { EnhancedProofreadResult } from '@/lib/chrome-ai/services/ProofreaderService' +import { ChromeAiDiagnostics } from './ChromeAiDiagnostics' +import { + InlineSpinner, + ModelDownloadProgress, + DownloadStatus, +} from '@/components/ui' + +/** + * Test component for Chrome Writing Enhancement APIs + * This component allows manual testing of all WritingEnhancementService features + */ +export const WritingEnhancementTest = () => { + const [serviceStatus, setServiceStatus] = useState<'idle' | 'initializing' | 'ready' | 'error'>('idle') + const [isProcessing, setIsProcessing] = useState(false) + const [inputText, setInputText] = useState(DEFAULT_TEST_TEXT) + const [selectedTone, setSelectedTone] = useState<'professional' | 'casual' | 'academic' | 'simple'>('professional') + const [error, setError] = useState(null) + const [logs, setLogs] = useState([]) + const [downloadProgress, setDownloadProgress] = useState(null) + const [downloadStatus, setDownloadStatus] = useState(DownloadStatus.CHECKING) + + // Results for individual operations + const [grammarResult, setGrammarResult] = useState(null) + const [toneResult, setToneResult] = useState(null) + const [keyPointsResult, setKeyPointsResult] = useState(null) + const [expandedResult, setExpandedResult] = useState(null) + const [suggestionsResult, setSuggestionsResult] = useState(null) + const [combinedResult, setCombinedResult] = useState(null) + + const serviceRef = useRef(null) + + const addLog = (message: string) => { + const timestamp = new Date().toLocaleTimeString() + setLogs((prev) => [`[${timestamp}] ${message}`, ...prev].slice(0, 30)) + } + + const clearResults = () => { + setGrammarResult(null) + setToneResult(null) + setKeyPointsResult(null) + setExpandedResult(null) + setSuggestionsResult(null) + setCombinedResult(null) + } + + const handleInitialize = async () => { + try { + setServiceStatus('initializing') + setError(null) + setDownloadProgress(null) + setDownloadStatus(DownloadStatus.CHECKING) + clearResults() + addLog('Initializing WritingEnhancementService...') + + const service = new WritingEnhancementService() + + await service.initialize() + serviceRef.current = service + + setDownloadStatus(DownloadStatus.READY) + setDownloadProgress(null) + setServiceStatus('ready') + addLog('✓ WritingEnhancementService initialized successfully') + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Unknown error' + setServiceStatus('error') + setDownloadStatus(DownloadStatus.FAILED) + setDownloadProgress(null) + setError(errorMsg) + addLog(`✗ Initialization failed: ${errorMsg}`) + } + } + + const handleCorrectGrammar = async () => { + if (!serviceRef.current) { + setError('Service not initialized') + return + } + + try { + setIsProcessing(true) + setError(null) + setGrammarResult(null) + addLog('Starting grammar correction...') + + const result = await serviceRef.current.correctGrammar(inputText) + + setGrammarResult(result) + addLog(`✓ Grammar correction complete (${result.stats.totalCorrections} corrections found)`) + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Unknown error' + setError(errorMsg) + addLog(`✗ Grammar correction failed: ${errorMsg}`) + } finally { + setIsProcessing(false) + } + } + + const handleAdjustTone = async () => { + if (!serviceRef.current) { + setError('Service not initialized') + return + } + + try { + setIsProcessing(true) + setError(null) + setToneResult(null) + addLog(`Starting tone adjustment to ${selectedTone}...`) + + const result = await serviceRef.current.adjustTone(inputText, selectedTone) + + setToneResult(result) + addLog(`✓ Tone adjustment complete (${result.length} characters)`) + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Unknown error' + setError(errorMsg) + addLog(`✗ Tone adjustment failed: ${errorMsg}`) + } finally { + setIsProcessing(false) + } + } + + const handleExtractKeyPoints = async () => { + if (!serviceRef.current) { + setError('Service not initialized') + return + } + + try { + setIsProcessing(true) + setError(null) + setKeyPointsResult(null) + addLog('Extracting key points...') + + const result = await serviceRef.current.extractKeyPoints(inputText) + + setKeyPointsResult(result) + addLog(`✓ Key point extraction complete (${result.length} points found)`) + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Unknown error' + setError(errorMsg) + addLog(`✗ Key point extraction failed: ${errorMsg}`) + } finally { + setIsProcessing(false) + } + } + + const handleExpandText = async () => { + if (!serviceRef.current) { + setError('Service not initialized') + return + } + + try { + setIsProcessing(true) + setError(null) + setExpandedResult(null) + addLog('Expanding text...') + + const result = await serviceRef.current.expandText(inputText, 'medium') + + setExpandedResult(result) + addLog(`✓ Text expansion complete (${inputText.length} → ${result.length} characters)`) + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Unknown error' + setError(errorMsg) + addLog(`✗ Text expansion failed: ${errorMsg}`) + } finally { + setIsProcessing(false) + } + } + + const handleGetSuggestions = async () => { + if (!serviceRef.current) { + setError('Service not initialized') + return + } + + try { + setIsProcessing(true) + setError(null) + setSuggestionsResult(null) + addLog('Getting writing suggestions...') + + const result = await serviceRef.current.getWritingSuggestions(inputText) + + setSuggestionsResult(result) + addLog(`✓ Writing suggestions complete (${result.length} suggestions found)`) + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Unknown error' + setError(errorMsg) + addLog(`✗ Writing suggestions failed: ${errorMsg}`) + } finally { + setIsProcessing(false) + } + } + + const handleEnhanceAll = async () => { + if (!serviceRef.current) { + setError('Service not initialized') + return + } + + try { + setIsProcessing(true) + setError(null) + setCombinedResult(null) + addLog('Running all enhancements...') + + const result = await serviceRef.current.enhanceText(inputText, { + correctGrammar: true, + extractKeyPoints: true, + getSuggestions: true, + adjustTone: selectedTone, + expand: false, // Skip expansion in combined test + }) + + setCombinedResult(result) + addLog(`✓ All enhancements complete`) + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'Unknown error' + setError(errorMsg) + addLog(`✗ Combined enhancement failed: ${errorMsg}`) + } finally { + setIsProcessing(false) + } + } + + const handleDestroy = useCallback(() => { + if (serviceRef.current) { + serviceRef.current.destroy() + serviceRef.current = null + setServiceStatus('idle') + clearResults() + addLog('Service destroyed') + } + }, []) + + // Cleanup on unmount + useEffect(() => { + return () => { + handleDestroy() + } + }, [handleDestroy]) + + return ( +
+ {/* Header */} +
+

Writing Enhancement Test Suite

+

+ Test the Chrome Writing Enhancement APIs (Writer, Proofreader, Prompt, Rewriter) +

+
+ + {/* Diagnostics */} +
+ +
+ + {/* Service Status */} +
+
+
+

Service Status

+

+ Status:{' '} + + {serviceStatus} + +

+
+
+ + +
+
+ + {/* Download Progress */} + {serviceStatus === 'initializing' && downloadProgress && ( +
+ +
+ )} + + {/* Initialization Status (no download) */} + {serviceStatus === 'initializing' && !downloadProgress && ( +
+ + {downloadStatus} +
+ )} + + {error && ( +
+ Error: {error} +
+ )} +
+ + {/* Input Section */} +
+

Input Text

+