pomera-ai-commander 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -421,6 +421,8 @@ class BackupRecoveryManager:
421
421
  """
422
422
  Export settings to a file.
423
423
 
424
+ Also exports notes from notes.db if available.
425
+
424
426
  Args:
425
427
  settings_data: Settings data to export
426
428
  export_path: Path to export file
@@ -445,18 +447,25 @@ class BackupRecoveryManager:
445
447
  export_file.parent.mkdir(parents=True, exist_ok=True)
446
448
  self.logger.debug(f"Export directory created/verified: {export_file.parent}")
447
449
 
450
+ # Include notes data from notes.db
451
+ notes_data = self._export_notes_data()
452
+ if notes_data:
453
+ settings_data['notes'] = notes_data
454
+ self.logger.info(f"Including {len(notes_data)} notes in export")
455
+
448
456
  # Count items being exported for logging
449
457
  tool_count = len(settings_data.get("tool_settings", {}))
458
+ notes_count = len(settings_data.get("notes", []))
450
459
  total_keys = len(settings_data.keys())
451
460
 
452
461
  if format_type == "compressed":
453
462
  with gzip.open(export_path, 'wt', encoding='utf-8') as f:
454
463
  json.dump(settings_data, f, indent=2, ensure_ascii=False)
455
- self.logger.info(f"Settings exported (compressed) to: {export_path} - {total_keys} keys, {tool_count} tools")
464
+ self.logger.info(f"Settings exported (compressed) to: {export_path} - {total_keys} keys, {tool_count} tools, {notes_count} notes")
456
465
  else:
457
466
  with open(export_path, 'w', encoding='utf-8') as f:
458
467
  json.dump(settings_data, f, indent=2, ensure_ascii=False)
459
- self.logger.info(f"Settings exported to: {export_path} - {total_keys} keys, {tool_count} tools")
468
+ self.logger.info(f"Settings exported to: {export_path} - {total_keys} keys, {tool_count} tools, {notes_count} notes")
460
469
 
461
470
  # Verify file was created and has content
462
471
  if export_file.exists():
@@ -481,10 +490,159 @@ class BackupRecoveryManager:
481
490
  self.logger.error(f"Export failed with unexpected error: {e}", exc_info=True)
482
491
  return False
483
492
 
493
+ def _export_notes_data(self) -> Optional[List[Dict[str, Any]]]:
494
+ """
495
+ Export notes from notes.db.
496
+
497
+ Returns:
498
+ List of note dictionaries, or None if notes.db not available
499
+ """
500
+ try:
501
+ # Get notes database path
502
+ try:
503
+ from core.data_directory import get_database_path
504
+ notes_db_path = get_database_path('notes.db')
505
+ except ImportError:
506
+ # Fallback to backup directory parent
507
+ notes_db_path = str(self.backup_dir.parent / 'notes.db')
508
+
509
+ if not os.path.exists(notes_db_path):
510
+ self.logger.debug(f"Notes database not found: {notes_db_path}")
511
+ return None
512
+
513
+ import sqlite3
514
+ conn = sqlite3.connect(notes_db_path, timeout=10.0)
515
+ conn.row_factory = sqlite3.Row
516
+
517
+ try:
518
+ cursor = conn.execute('''
519
+ SELECT id, Created, Modified, Title, Input, Output
520
+ FROM notes ORDER BY id
521
+ ''')
522
+ notes = []
523
+ for row in cursor.fetchall():
524
+ notes.append({
525
+ 'id': row['id'],
526
+ 'Created': row['Created'],
527
+ 'Modified': row['Modified'],
528
+ 'Title': row['Title'],
529
+ 'Input': row['Input'],
530
+ 'Output': row['Output']
531
+ })
532
+
533
+ self.logger.debug(f"Exported {len(notes)} notes from notes.db")
534
+ return notes
535
+
536
+ finally:
537
+ conn.close()
538
+
539
+ except Exception as e:
540
+ self.logger.warning(f"Failed to export notes data: {e}")
541
+ return None
542
+
543
+ def _import_notes_data(self, notes_data: List[Dict[str, Any]]) -> int:
544
+ """
545
+ Import notes to notes.db.
546
+
547
+ Notes are imported with their original IDs if no conflict exists,
548
+ otherwise they are inserted with new IDs.
549
+
550
+ Args:
551
+ notes_data: List of note dictionaries to import
552
+
553
+ Returns:
554
+ Number of notes successfully imported
555
+ """
556
+ if not notes_data:
557
+ return 0
558
+
559
+ try:
560
+ # Get notes database path
561
+ try:
562
+ from core.data_directory import get_database_path
563
+ notes_db_path = get_database_path('notes.db')
564
+ except ImportError:
565
+ # Fallback to backup directory parent
566
+ notes_db_path = str(self.backup_dir.parent / 'notes.db')
567
+
568
+ import sqlite3
569
+ conn = sqlite3.connect(notes_db_path, timeout=10.0)
570
+
571
+ try:
572
+ # Ensure tables exist
573
+ conn.execute('''
574
+ CREATE TABLE IF NOT EXISTS notes (
575
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
576
+ Created DATETIME DEFAULT CURRENT_TIMESTAMP,
577
+ Modified DATETIME DEFAULT CURRENT_TIMESTAMP,
578
+ Title TEXT(255),
579
+ Input TEXT,
580
+ Output TEXT
581
+ )
582
+ ''')
583
+
584
+ # Check if FTS table exists
585
+ fts_exists = conn.execute(
586
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='notes_fts'"
587
+ ).fetchone() is not None
588
+
589
+ imported_count = 0
590
+ for note in notes_data:
591
+ try:
592
+ # Check if note with this ID already exists
593
+ existing = conn.execute(
594
+ 'SELECT id FROM notes WHERE id = ?',
595
+ (note.get('id'),)
596
+ ).fetchone()
597
+
598
+ if existing:
599
+ # Skip notes that already exist
600
+ self.logger.debug(f"Skipping existing note ID {note.get('id')}")
601
+ continue
602
+
603
+ # Insert with original ID if possible
604
+ conn.execute('''
605
+ INSERT INTO notes (id, Created, Modified, Title, Input, Output)
606
+ VALUES (?, ?, ?, ?, ?, ?)
607
+ ''', (
608
+ note.get('id'),
609
+ note.get('Created'),
610
+ note.get('Modified'),
611
+ note.get('Title'),
612
+ note.get('Input'),
613
+ note.get('Output')
614
+ ))
615
+ imported_count += 1
616
+
617
+ except Exception as e:
618
+ self.logger.debug(f"Failed to import note {note.get('id')}: {e}")
619
+
620
+ conn.commit()
621
+
622
+ # Rebuild FTS index if table exists
623
+ if fts_exists:
624
+ try:
625
+ conn.execute('INSERT INTO notes_fts(notes_fts) VALUES("rebuild")')
626
+ conn.commit()
627
+ except Exception as e:
628
+ self.logger.debug(f"FTS rebuild skipped: {e}")
629
+
630
+ self.logger.debug(f"Imported {imported_count} notes to notes.db")
631
+ return imported_count
632
+
633
+ finally:
634
+ conn.close()
635
+
636
+ except Exception as e:
637
+ self.logger.warning(f"Failed to import notes data: {e}")
638
+ return 0
639
+
484
640
  def import_settings(self, import_path: str) -> Optional[Dict[str, Any]]:
485
641
  """
486
642
  Import settings from a file.
487
643
 
644
+ Also imports notes to notes.db if present in the import file.
645
+
488
646
  Args:
489
647
  import_path: Path to import file
490
648
 
@@ -524,6 +682,13 @@ class BackupRecoveryManager:
524
682
  self.logger.error(f"Import failed: Invalid data format - expected dict, got {type(settings_data)}")
525
683
  return None
526
684
 
685
+ # Import notes if present
686
+ if 'notes' in settings_data:
687
+ notes_data = settings_data.pop('notes') # Remove from settings_data
688
+ if notes_data:
689
+ imported_count = self._import_notes_data(notes_data)
690
+ self.logger.info(f"Imported {imported_count} notes to notes.db")
691
+
527
692
  # Count imported items for logging
528
693
  tool_count = len(settings_data.get("tool_settings", {}))
529
694
  total_keys = len(settings_data.keys())
@@ -865,7 +1030,8 @@ class BackupRecoveryManager:
865
1030
  # Get table counts
866
1031
  table_counts = {}
867
1032
  tables = ['core_settings', 'tool_settings', 'tab_content',
868
- 'performance_settings', 'font_settings', 'dialog_settings']
1033
+ 'performance_settings', 'font_settings', 'dialog_settings',
1034
+ 'notes', 'notes_fts']
869
1035
 
870
1036
  for table in tables:
871
1037
  try: