feat: miller column browser, offline cache, search, breadcrumbs
- Dynamic miller column sizing with flexbox layout - Root column (160px), middle slices (48px), active column (flex) - Auto-scroll browser to rightmost column on navigation - Offline indicator UI and IndexedDB cache integration - /api/documents endpoint for client-side document caching - Breadcrumb navigation in document content area - FTS5 search with SQLite virtual table - Client-side Mermaid and KaTeX rendering via CDN - Deep sample content (advanced-topics/raft-deep-dive) - Border removal (only search button keeps radius) - Full viewport layout with proper borders between sidebar/content
This commit is contained in:
@@ -91,6 +91,7 @@ func New(ctx context.Context, cfg config.Config, logger *slog.Logger) (*App, err
|
||||
|
||||
func (a *App) Run(ctx context.Context) error {
|
||||
go a.watchDocuments(ctx)
|
||||
go a.syncPoll(ctx)
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
@@ -107,7 +108,26 @@ func (a *App) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (a *App) watchDocuments(ctx context.Context) {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
watcher, err := NewFileWatcher(a.cfg.Content.SourceDir, a.logger)
|
||||
if err != nil {
|
||||
a.logger.Warn("file watcher failed, falling back to polling only", "error", err)
|
||||
<-ctx.Done()
|
||||
return
|
||||
}
|
||||
|
||||
watcher.SetOnChange(func(path string) {
|
||||
if _, err := a.docs.SyncSourceDir(ctx); err != nil {
|
||||
a.logger.Warn("document sync failed", "error", err)
|
||||
} else {
|
||||
a.logger.Debug("document synced", "path", path)
|
||||
}
|
||||
})
|
||||
|
||||
watcher.Run(ctx)
|
||||
}
|
||||
|
||||
func (a *App) syncPoll(ctx context.Context) {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
Reference in New Issue
Block a user