package service import ( "context" "fmt" "os" "os/signal" "syscall" "github.com/yeho/klp/internal/clipboard" "github.com/yeho/klp/internal/config" "github.com/yeho/klp/internal/database" ) type Monitor struct { cfg *config.Config db *database.Database clipboard clipboard.Clipboard } func NewMonitor(cfg *config.Config, db *database.Database) (*Monitor, error) { clip, err := clipboard.New() if err != nil { return nil, fmt.Errorf("failed to initialize clipboard: %w", err) } return &Monitor{ cfg: cfg, db: db, clipboard: clip, }, nil } func (m *Monitor) Run() error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Handle shutdown signals sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) go func() { <-sigChan fmt.Println("\nShutting down...") cancel() }() fmt.Println("Clipboard monitor started. Press Ctrl+C to stop.") changes := m.clipboard.Watch(ctx) for { select { case <-ctx.Done(): return nil case content, ok := <-changes: if !ok { return nil } if err := m.handleChange(content); err != nil { fmt.Fprintf(os.Stderr, "Error handling clipboard change: %v\n", err) } } } } func (m *Monitor) handleChange(content string) error { // Skip empty content if content == "" { return nil } // Check max entry size if configured if m.cfg.MaxEntrySize > 0 && len(content) > m.cfg.MaxEntrySize { return nil } // Skip if duplicate of most recent entry recent := m.db.MostRecent() if recent != nil && recent.Value == content { return nil } // Add to database entry, err := m.db.Add(content) if err != nil { return err } // Log the addition (truncate for display) preview := content if len(preview) > 50 { preview = preview[:50] + "..." } // Replace newlines for cleaner display preview = replaceNewlines(preview) fmt.Printf("Added [%s]: %s\n", entry.ID, preview) return nil } func replaceNewlines(s string) string { result := make([]byte, 0, len(s)) for i := 0; i < len(s); i++ { if s[i] == '\n' { result = append(result, ' ') } else if s[i] == '\r' { // skip } else { result = append(result, s[i]) } } return string(result) }