@@ -103,6 +103,8 @@ import EasyNotesSidebar from './components/EasyNotesSidebar';
103103import EasyAIPanel from './components/EasyAIPanel' ;
104104import { buildSystemPrompt , parseFixTarget , extractBlock , extractTable } from './components/easyai/aiPersonas' ;
105105import { queryEasyAI } from './components/easyai/aiService' ;
106+ import { scanRepository } from './components/easyai/repoScanner' ;
107+ import { generateDocumentation } from './components/easyai/docGenerator' ;
106108import FeaturesModal from './components/FeaturesModal' ;
107109import ThemeModal from './components/ThemeModal' ;
108110import ImportThemeModal from './components/ImportThemeModal' ;
@@ -256,6 +258,16 @@ const App = () => {
256258 const [ pendingCredentialAction , setPendingCredentialAction ] = useState < ( ( ) => void ) | null > ( null ) ;
257259 const [ prefillCredentials , setPrefillCredentials ] = useState < { username : string ; token : string } | null > ( null ) ;
258260 const [ currentDirHandle , setCurrentDirHandle ] = useState < any > ( null ) ; // For web File System Access API
261+
262+ // Repo scan progress state
263+ const [ scanProgress , setScanProgress ] = useState < {
264+ isScanning : boolean ;
265+ currentFile : string ;
266+ filesProcessed : number ;
267+ totalFiles : number ;
268+ } > ( { isScanning : false , currentFile : '' , filesProcessed : 0 , totalFiles : 0 } ) ;
269+ const scanAbortControllerRef = useRef < AbortController | null > ( null ) ;
270+
259271 const [ confirmModalConfig , setConfirmModalConfig ] = useState < {
260272 open : boolean ;
261273 title : string ;
@@ -3220,11 +3232,170 @@ const App = () => {
32203232 )
32213233 }
32223234
3235+ { scanProgress . isScanning && (
3236+ < div style = { {
3237+ position : 'fixed' ,
3238+ top : 0 ,
3239+ left : 0 ,
3240+ right : 0 ,
3241+ bottom : 0 ,
3242+ backgroundColor : 'rgba(0, 0, 0, 0.5)' ,
3243+ display : 'flex' ,
3244+ alignItems : 'center' ,
3245+ justifyContent : 'center' ,
3246+ zIndex : 10000 ,
3247+ } } >
3248+ < div style = { {
3249+ background : 'var(--bg-color, #1e1e1e)' ,
3250+ color : 'var(--text-color, #ccc)' ,
3251+ borderRadius : '8px' ,
3252+ padding : '24px 32px' ,
3253+ minWidth : '360px' ,
3254+ maxWidth : '480px' ,
3255+ boxShadow : '0 4px 24px rgba(0,0,0,0.4)' ,
3256+ } } >
3257+ < h3 style = { { margin : '0 0 16px 0' , fontSize : '16px' } } > Scanning Repository…</ h3 >
3258+ < div style = { {
3259+ background : 'var(--border-color, #333)' ,
3260+ borderRadius : '4px' ,
3261+ height : '8px' ,
3262+ overflow : 'hidden' ,
3263+ marginBottom : '12px' ,
3264+ } } >
3265+ < div style = { {
3266+ background : 'var(--accent-color, #007acc)' ,
3267+ height : '100%' ,
3268+ width : `${ scanProgress . totalFiles > 0 ? ( scanProgress . filesProcessed / scanProgress . totalFiles ) * 100 : 0 } %` ,
3269+ transition : 'width 0.3s ease' ,
3270+ borderRadius : '4px' ,
3271+ } } />
3272+ </ div >
3273+ < div style = { { fontSize : '13px' , marginBottom : '8px' } } >
3274+ { scanProgress . filesProcessed } / { scanProgress . totalFiles } files
3275+ </ div >
3276+ < div style = { {
3277+ fontSize : '12px' ,
3278+ color : 'var(--text-muted, #888)' ,
3279+ marginBottom : '16px' ,
3280+ overflow : 'hidden' ,
3281+ textOverflow : 'ellipsis' ,
3282+ whiteSpace : 'nowrap' ,
3283+ } } >
3284+ { scanProgress . currentFile || 'Preparing…' }
3285+ </ div >
3286+ < button
3287+ onClick = { ( ) => scanAbortControllerRef . current ?. abort ( ) }
3288+ style = { {
3289+ background : 'var(--danger-color, #d32f2f)' ,
3290+ color : '#fff' ,
3291+ border : 'none' ,
3292+ borderRadius : '4px' ,
3293+ padding : '6px 18px' ,
3294+ cursor : 'pointer' ,
3295+ fontSize : '13px' ,
3296+ } }
3297+ >
3298+ Cancel
3299+ </ button >
3300+ </ div >
3301+ </ div >
3302+ ) }
3303+
32233304 < EasyAIPanel
32243305 showEasyAIPanel = { showEasyAIPanel }
32253306 setShowEasyAIPanel = { setShowEasyAIPanel }
32263307 showToast = { showToast }
32273308 onActionSelect = { async ( actionId , promptText ) => {
3309+ // ── Documentation persona with repo scanning ──
3310+ if ( actionId === 'documentation' ) {
3311+ const isTauri = ! ! ( window as any ) . __TAURI_INTERNALS__ ;
3312+ console . log ( '[EasyAI-Doc] Documentation action triggered' ) ;
3313+ console . log ( '[EasyAI-Doc] isTauri:' , isTauri ) ;
3314+ console . log ( '[EasyAI-Doc] currentRepoPath:' , currentRepoPath ) ;
3315+ console . log ( '[EasyAI-Doc] currentDirHandle:' , currentDirHandle ) ;
3316+ console . log ( '[EasyAI-Doc] isGitRepo state:' , isGitRepo ) ;
3317+
3318+ // Tauri uses file paths; web uses FileSystemDirectoryHandle
3319+ const hasTauriRepo = isTauri && currentRepoPath ;
3320+ const hasWebRepo = ! isTauri && currentDirHandle ;
3321+
3322+ if ( ! hasTauriRepo && ! hasWebRepo ) {
3323+ console . warn ( '[EasyAI-Doc] No repository available — aborting' ) ;
3324+ showToast ( 'No Git repository loaded. Please open a repository first via EasyGit.' , 'warning' ) ;
3325+ return ;
3326+ }
3327+
3328+ const controller = new AbortController ( ) ;
3329+ scanAbortControllerRef . current = controller ;
3330+
3331+ setScanProgress ( { isScanning : true , currentFile : '' , filesProcessed : 0 , totalFiles : 0 } ) ;
3332+ setShowEasyAIPanel ( false ) ;
3333+
3334+ try {
3335+ let scanResult ;
3336+
3337+ if ( hasTauriRepo ) {
3338+ console . log ( '[EasyAI-Doc] Using Tauri scanner for path:' , currentRepoPath ) ;
3339+ const { scanRepositoryTauri } = await import ( './components/easyai/tauriRepoScanner' ) ;
3340+ scanResult = await scanRepositoryTauri ( {
3341+ repoPath : currentRepoPath ! ,
3342+ userPrompt : promptText ,
3343+ onProgress : ( current , total , filePath ) => {
3344+ setScanProgress ( { isScanning : true , currentFile : filePath , filesProcessed : current , totalFiles : total } ) ;
3345+ } ,
3346+ signal : controller . signal ,
3347+ } ) ;
3348+ } else {
3349+ console . log ( '[EasyAI-Doc] Using web scanner with dirHandle:' , currentDirHandle . name ) ;
3350+ scanResult = await scanRepository ( {
3351+ dirHandle : currentDirHandle ,
3352+ userPrompt : promptText ,
3353+ onProgress : ( current , total , filePath ) => {
3354+ setScanProgress ( { isScanning : true , currentFile : filePath , filesProcessed : current , totalFiles : total } ) ;
3355+ } ,
3356+ signal : controller . signal ,
3357+ } ) ;
3358+ }
3359+
3360+ if ( scanResult . cancelled ) {
3361+ showToast ( 'Scan cancelled.' , 'info' ) ;
3362+ setScanProgress ( { isScanning : false , currentFile : '' , filesProcessed : 0 , totalFiles : 0 } ) ;
3363+ return ;
3364+ }
3365+
3366+ if ( scanResult . cache . size <= 1 ) {
3367+ showToast ( 'No scannable files found in the repository.' , 'warning' ) ;
3368+ setScanProgress ( { isScanning : false , currentFile : '' , filesProcessed : 0 , totalFiles : 0 } ) ;
3369+ return ;
3370+ }
3371+
3372+ // Log scan results for debugging
3373+ console . log ( `[RepoScanner] Cache contains ${ scanResult . cache . size - 1 } file summaries` ) ;
3374+
3375+ setScanProgress ( prev => ( { ...prev , currentFile : 'Generating documentation…' } ) ) ;
3376+ const doc = await generateDocumentation ( {
3377+ cache : scanResult . cache ,
3378+ userPrompt : promptText ,
3379+ signal : controller . signal ,
3380+ } ) ;
3381+
3382+ if ( doc ) {
3383+ setEditorContent ( doc + '\n' ) ;
3384+ showToast ( 'EasyAI (documentation) — documentation generated.' , 'success' ) ;
3385+ } else {
3386+ showToast ( 'EasyAI (documentation) — empty response.' , 'warning' ) ;
3387+ }
3388+ } catch ( err : any ) {
3389+ const msg = err . message || 'Scan failed' ;
3390+ console . error ( '[EasyAI-Doc] Scan error:' , msg , err ) ;
3391+ showToast ( msg , 'error' ) ;
3392+ } finally {
3393+ setScanProgress ( { isScanning : false , currentFile : '' , filesProcessed : 0 , totalFiles : 0 } ) ;
3394+ scanAbortControllerRef . current = null ;
3395+ }
3396+ return ;
3397+ }
3398+
32283399 const systemPrompt = buildSystemPrompt ( actionId , editorContent , promptText ) ;
32293400 if ( ! systemPrompt ) {
32303401 showToast ( `Unknown EasyAI action: ${ actionId } ` , 'error' ) ;
0 commit comments