aline-ai 0.6.2__py3-none-any.whl → 0.6.3__py3-none-any.whl
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.
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/METADATA +1 -1
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/RECORD +28 -30
- realign/__init__.py +1 -1
- realign/adapters/__init__.py +0 -3
- realign/cli.py +0 -1
- realign/commands/export_shares.py +154 -226
- realign/commands/watcher.py +28 -79
- realign/config.py +1 -47
- realign/dashboard/app.py +2 -8
- realign/dashboard/screens/event_detail.py +0 -3
- realign/dashboard/screens/session_detail.py +0 -1
- realign/dashboard/widgets/config_panel.py +109 -249
- realign/dashboard/widgets/events_table.py +71 -128
- realign/dashboard/widgets/sessions_table.py +76 -135
- realign/dashboard/widgets/watcher_panel.py +0 -2
- realign/db/sqlite_db.py +1 -2
- realign/events/event_summarizer.py +76 -35
- realign/events/session_summarizer.py +73 -32
- realign/hooks.py +383 -574
- realign/llm_client.py +201 -520
- realign/triggers/__init__.py +0 -2
- realign/triggers/next_turn_trigger.py +4 -5
- realign/triggers/registry.py +1 -4
- realign/watcher_core.py +3 -35
- realign/adapters/antigravity.py +0 -159
- realign/triggers/antigravity_trigger.py +0 -140
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/WHEEL +0 -0
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.6.2.dist-info → aline_ai-0.6.3.dist-info}/top_level.txt +0 -0
|
@@ -80,23 +80,16 @@ class EventsTable(Container):
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
EventsTable .table-container {
|
|
83
|
-
height:
|
|
83
|
+
height: 1fr;
|
|
84
84
|
overflow-x: auto;
|
|
85
|
-
overflow-y:
|
|
85
|
+
overflow-y: auto;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
EventsTable DataTable {
|
|
89
89
|
height: auto;
|
|
90
90
|
max-height: 100%;
|
|
91
91
|
overflow-x: auto;
|
|
92
|
-
overflow-y:
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
EventsTable .pagination-info {
|
|
96
|
-
height: 1;
|
|
97
|
-
margin-top: 1;
|
|
98
|
-
color: $text-muted;
|
|
99
|
-
text-align: center;
|
|
92
|
+
overflow-y: auto;
|
|
100
93
|
}
|
|
101
94
|
|
|
102
95
|
EventsTable .stats-info {
|
|
@@ -107,9 +100,9 @@ class EventsTable(Container):
|
|
|
107
100
|
"""
|
|
108
101
|
|
|
109
102
|
# Reactive properties
|
|
110
|
-
current_page: reactive[int] = reactive(1)
|
|
111
|
-
rows_per_page: reactive[int] = reactive(10)
|
|
112
103
|
wrap_mode: reactive[bool] = reactive(False)
|
|
104
|
+
# Maximum events to load (for scrollable list)
|
|
105
|
+
MAX_EVENTS: int = 500
|
|
113
106
|
|
|
114
107
|
def __init__(self) -> None:
|
|
115
108
|
super().__init__()
|
|
@@ -122,8 +115,9 @@ class EventsTable(Container):
|
|
|
122
115
|
self._refresh_worker: Optional[Worker] = None
|
|
123
116
|
self._share_export_worker: Optional[Worker] = None
|
|
124
117
|
self._refresh_timer = None
|
|
125
|
-
self.
|
|
126
|
-
self.
|
|
118
|
+
self._is_refreshing: bool = False
|
|
119
|
+
self._pending_refresh: bool = False
|
|
120
|
+
self._saved_cursor_event_id: Optional[str] = None
|
|
127
121
|
|
|
128
122
|
def compose(self) -> ComposeResult:
|
|
129
123
|
"""Compose the events table layout."""
|
|
@@ -150,7 +144,6 @@ class EventsTable(Container):
|
|
|
150
144
|
yield Static(id="section-header", classes="section-header")
|
|
151
145
|
with Container(classes="table-container"):
|
|
152
146
|
yield EventsListTable(id="events-table")
|
|
153
|
-
yield Static(id="pagination-info", classes="pagination-info")
|
|
154
147
|
yield Static(id="stats-info", classes="stats-info")
|
|
155
148
|
logger.debug("EventsTable.compose() completed")
|
|
156
149
|
except Exception as e:
|
|
@@ -164,22 +157,19 @@ class EventsTable(Container):
|
|
|
164
157
|
table = self.query_one("#events-table", EventsListTable)
|
|
165
158
|
table.owner = self
|
|
166
159
|
self._setup_table_columns(table)
|
|
167
|
-
|
|
168
|
-
# Calculate initial rows per page
|
|
169
|
-
self._calculate_rows_per_page()
|
|
170
160
|
logger.debug("EventsTable.on_mount() completed")
|
|
171
161
|
except Exception as e:
|
|
172
162
|
logger.error(f"EventsTable.on_mount() failed: {e}\n{traceback.format_exc()}")
|
|
173
163
|
raise
|
|
174
164
|
|
|
175
165
|
def on_resize(self) -> None:
|
|
176
|
-
"""Handle window resize
|
|
177
|
-
|
|
166
|
+
"""Handle window resize."""
|
|
167
|
+
pass # No longer need to recalculate pagination
|
|
178
168
|
|
|
179
169
|
def on_show(self) -> None:
|
|
180
|
-
"""
|
|
170
|
+
"""Refresh data when the tab becomes visible."""
|
|
181
171
|
if self._refresh_timer is None:
|
|
182
|
-
self._refresh_timer = self.set_interval(
|
|
172
|
+
self._refresh_timer = self.set_interval(60.0, self._on_refresh_timer)
|
|
183
173
|
else:
|
|
184
174
|
try:
|
|
185
175
|
self._refresh_timer.resume()
|
|
@@ -197,9 +187,7 @@ class EventsTable(Container):
|
|
|
197
187
|
pass
|
|
198
188
|
|
|
199
189
|
def _on_became_visible(self) -> None:
|
|
200
|
-
self._sync_to_available_height()
|
|
201
190
|
self._load_events()
|
|
202
|
-
self._update_display()
|
|
203
191
|
try:
|
|
204
192
|
self.query_one("#events-table", DataTable).focus()
|
|
205
193
|
except Exception:
|
|
@@ -388,41 +376,6 @@ class EventsTable(Container):
|
|
|
388
376
|
share_btn = self.query_one("#share-event-btn", Button)
|
|
389
377
|
share_btn.disabled = selected_count == 0
|
|
390
378
|
|
|
391
|
-
def _sync_to_available_height(self) -> None:
|
|
392
|
-
"""Recalculate rows per page and reload if the page size changed."""
|
|
393
|
-
old_rows_per_page = self.rows_per_page
|
|
394
|
-
self._calculate_rows_per_page()
|
|
395
|
-
|
|
396
|
-
if self.rows_per_page != old_rows_per_page:
|
|
397
|
-
total_pages = self._get_total_pages()
|
|
398
|
-
if self.current_page > total_pages:
|
|
399
|
-
self.current_page = total_pages
|
|
400
|
-
self._load_events()
|
|
401
|
-
|
|
402
|
-
self._update_display()
|
|
403
|
-
|
|
404
|
-
def _calculate_rows_per_page(self) -> None:
|
|
405
|
-
"""Calculate rows per page based on available height."""
|
|
406
|
-
try:
|
|
407
|
-
panel_height = self.size.height
|
|
408
|
-
|
|
409
|
-
# Calculate exact space needed:
|
|
410
|
-
# - Summary section: ~1 line content + 2 border + 2 padding + 1 margin = 6
|
|
411
|
-
# - Section header: 1 line + 1 margin = 2
|
|
412
|
-
# - Table header row: 1
|
|
413
|
-
# - Pagination info: 1 line + 1 margin = 2
|
|
414
|
-
# - Panel padding: 2 (top + bottom)
|
|
415
|
-
# - Extra buffer: 3
|
|
416
|
-
|
|
417
|
-
fixed_height = 6 + 2 + 1 + 2 + 2 + 3 # = 16
|
|
418
|
-
|
|
419
|
-
available_for_rows = panel_height - fixed_height
|
|
420
|
-
rows = max(available_for_rows, 3) # At least 3 rows
|
|
421
|
-
|
|
422
|
-
self.rows_per_page = rows
|
|
423
|
-
except Exception:
|
|
424
|
-
self.rows_per_page = 10
|
|
425
|
-
|
|
426
379
|
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
427
380
|
button_id = event.button.id or ""
|
|
428
381
|
if button_id == "share-import-btn":
|
|
@@ -442,25 +395,6 @@ class EventsTable(Container):
|
|
|
442
395
|
|
|
443
396
|
self.app.push_screen(ShareImportScreen())
|
|
444
397
|
|
|
445
|
-
def _get_total_pages(self) -> int:
|
|
446
|
-
"""Calculate total pages."""
|
|
447
|
-
if self._total_events == 0:
|
|
448
|
-
return 1
|
|
449
|
-
return (self._total_events + self.rows_per_page - 1) // self.rows_per_page
|
|
450
|
-
|
|
451
|
-
def action_next_page(self) -> None:
|
|
452
|
-
"""Go to next page."""
|
|
453
|
-
total_pages = self._get_total_pages()
|
|
454
|
-
if self.current_page < total_pages:
|
|
455
|
-
self.current_page += 1
|
|
456
|
-
self._load_events()
|
|
457
|
-
|
|
458
|
-
def action_prev_page(self) -> None:
|
|
459
|
-
"""Go to previous page."""
|
|
460
|
-
if self.current_page > 1:
|
|
461
|
-
self.current_page -= 1
|
|
462
|
-
self._load_events()
|
|
463
|
-
|
|
464
398
|
def _on_refresh_timer(self) -> None:
|
|
465
399
|
self.refresh_data(force=False)
|
|
466
400
|
|
|
@@ -469,26 +403,37 @@ class EventsTable(Container):
|
|
|
469
403
|
if not self.display:
|
|
470
404
|
return
|
|
471
405
|
|
|
472
|
-
snapshot = (int(self.current_page), int(self.rows_per_page))
|
|
473
406
|
if self._refresh_worker is not None and self._refresh_worker.state in (
|
|
474
407
|
WorkerState.PENDING,
|
|
475
408
|
WorkerState.RUNNING,
|
|
476
409
|
):
|
|
477
410
|
if force:
|
|
478
|
-
self.
|
|
411
|
+
self._pending_refresh = True
|
|
479
412
|
return
|
|
480
413
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
self._pending_refresh_snapshot = None
|
|
414
|
+
# Save current cursor position before refresh
|
|
415
|
+
self._save_cursor_position()
|
|
484
416
|
|
|
485
417
|
def work() -> dict:
|
|
486
|
-
return self.
|
|
418
|
+
return self._collect_all_events()
|
|
487
419
|
|
|
488
|
-
self.
|
|
489
|
-
self.
|
|
420
|
+
self._is_refreshing = True
|
|
421
|
+
self._pending_refresh = False
|
|
490
422
|
self._refresh_worker = self.run_worker(work, thread=True, exit_on_error=False)
|
|
491
423
|
|
|
424
|
+
def _save_cursor_position(self) -> None:
|
|
425
|
+
"""Save the current cursor event ID to restore after refresh."""
|
|
426
|
+
try:
|
|
427
|
+
table = self.query_one("#events-table", DataTable)
|
|
428
|
+
if table.row_count > 0:
|
|
429
|
+
self._saved_cursor_event_id = str(
|
|
430
|
+
table.coordinate_to_cell_key(table.cursor_coordinate)[0].value
|
|
431
|
+
)
|
|
432
|
+
else:
|
|
433
|
+
self._saved_cursor_event_id = None
|
|
434
|
+
except Exception:
|
|
435
|
+
self._saved_cursor_event_id = None
|
|
436
|
+
|
|
492
437
|
def _load_events(self) -> None:
|
|
493
438
|
"""Compatibility hook (tests stub this); default triggers async refresh."""
|
|
494
439
|
if not self.is_mounted:
|
|
@@ -510,30 +455,29 @@ class EventsTable(Container):
|
|
|
510
455
|
|
|
511
456
|
if event.state == WorkerState.ERROR:
|
|
512
457
|
result = {
|
|
513
|
-
"snapshot": self._active_refresh_snapshot,
|
|
514
458
|
"total_events": 0,
|
|
515
459
|
"events": [],
|
|
516
460
|
}
|
|
461
|
+
self._is_refreshing = False
|
|
517
462
|
elif event.state != WorkerState.SUCCESS:
|
|
518
463
|
return
|
|
519
464
|
else:
|
|
520
465
|
result = self._refresh_worker.result or {}
|
|
466
|
+
self._is_refreshing = False
|
|
521
467
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
self._events_by_id = {}
|
|
534
|
-
self._update_display()
|
|
468
|
+
try:
|
|
469
|
+
self._total_events = int(result.get("total_events") or 0)
|
|
470
|
+
except Exception:
|
|
471
|
+
self._total_events = 0
|
|
472
|
+
try:
|
|
473
|
+
self._events = list(result.get("events") or [])
|
|
474
|
+
self._events_by_id = {e["id"]: e for e in self._events}
|
|
475
|
+
except Exception:
|
|
476
|
+
self._events = []
|
|
477
|
+
self._events_by_id = {}
|
|
478
|
+
self._update_display()
|
|
535
479
|
|
|
536
|
-
if self.
|
|
480
|
+
if self._pending_refresh:
|
|
537
481
|
self.refresh_data()
|
|
538
482
|
|
|
539
483
|
def _start_share_export(self) -> None:
|
|
@@ -718,8 +662,8 @@ class EventsTable(Container):
|
|
|
718
662
|
return ""
|
|
719
663
|
return ""
|
|
720
664
|
|
|
721
|
-
def
|
|
722
|
-
"""Collect events
|
|
665
|
+
def _collect_all_events(self) -> dict:
|
|
666
|
+
"""Collect all events (background thread)."""
|
|
723
667
|
total_events: int = 0
|
|
724
668
|
events: list[dict] = []
|
|
725
669
|
|
|
@@ -733,8 +677,7 @@ class EventsTable(Container):
|
|
|
733
677
|
row = conn.execute("SELECT COUNT(*) FROM events").fetchone()
|
|
734
678
|
total_events = int(row[0]) if row else 0
|
|
735
679
|
|
|
736
|
-
# Get
|
|
737
|
-
offset = (int(page) - 1) * int(rows_per_page)
|
|
680
|
+
# Get all events (up to MAX_EVENTS)
|
|
738
681
|
try:
|
|
739
682
|
rows = conn.execute(
|
|
740
683
|
"""
|
|
@@ -749,9 +692,9 @@ class EventsTable(Container):
|
|
|
749
692
|
(SELECT COUNT(*) FROM event_sessions WHERE event_id = e.id) AS session_count
|
|
750
693
|
FROM events e
|
|
751
694
|
ORDER BY e.created_at DESC
|
|
752
|
-
LIMIT ?
|
|
695
|
+
LIMIT ?
|
|
753
696
|
""",
|
|
754
|
-
(
|
|
697
|
+
(self.MAX_EVENTS,),
|
|
755
698
|
).fetchall()
|
|
756
699
|
has_new_columns = True
|
|
757
700
|
except Exception:
|
|
@@ -766,9 +709,9 @@ class EventsTable(Container):
|
|
|
766
709
|
(SELECT COUNT(*) FROM event_sessions WHERE event_id = e.id) AS session_count
|
|
767
710
|
FROM events e
|
|
768
711
|
ORDER BY e.created_at DESC
|
|
769
|
-
LIMIT ?
|
|
712
|
+
LIMIT ?
|
|
770
713
|
""",
|
|
771
|
-
(
|
|
714
|
+
(self.MAX_EVENTS,),
|
|
772
715
|
).fetchall()
|
|
773
716
|
has_new_columns = False
|
|
774
717
|
|
|
@@ -826,7 +769,7 @@ class EventsTable(Container):
|
|
|
826
769
|
|
|
827
770
|
events.append(
|
|
828
771
|
{
|
|
829
|
-
"index":
|
|
772
|
+
"index": i + 1,
|
|
830
773
|
"id": event_id,
|
|
831
774
|
"short_id": self._shorten_id(event_id),
|
|
832
775
|
"title": title,
|
|
@@ -844,7 +787,6 @@ class EventsTable(Container):
|
|
|
844
787
|
events = []
|
|
845
788
|
|
|
846
789
|
return {
|
|
847
|
-
"snapshot": (int(page), int(rows_per_page)),
|
|
848
790
|
"total_events": total_events,
|
|
849
791
|
"events": events,
|
|
850
792
|
}
|
|
@@ -860,18 +802,23 @@ class EventsTable(Container):
|
|
|
860
802
|
|
|
861
803
|
# Update table
|
|
862
804
|
table = self.query_one("#events-table", DataTable)
|
|
863
|
-
|
|
864
|
-
try
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
805
|
+
|
|
806
|
+
# Use saved cursor position if available, otherwise try to get current
|
|
807
|
+
restore_event_id = self._saved_cursor_event_id
|
|
808
|
+
if restore_event_id is None:
|
|
809
|
+
try:
|
|
810
|
+
if table.row_count > 0:
|
|
811
|
+
restore_event_id = str(
|
|
812
|
+
table.coordinate_to_cell_key(table.cursor_coordinate)[0].value
|
|
813
|
+
)
|
|
814
|
+
except Exception:
|
|
815
|
+
restore_event_id = None
|
|
816
|
+
|
|
871
817
|
table.clear()
|
|
872
818
|
|
|
873
|
-
# Always enable
|
|
819
|
+
# Always enable scrollbars
|
|
874
820
|
table.styles.overflow_x = "auto"
|
|
821
|
+
table.styles.overflow_y = "auto"
|
|
875
822
|
table.show_horizontal_scrollbar = True
|
|
876
823
|
|
|
877
824
|
for event in self._events:
|
|
@@ -902,10 +849,10 @@ class EventsTable(Container):
|
|
|
902
849
|
)
|
|
903
850
|
|
|
904
851
|
if table.row_count > 0:
|
|
905
|
-
if
|
|
852
|
+
if restore_event_id:
|
|
906
853
|
try:
|
|
907
854
|
table.cursor_coordinate = (
|
|
908
|
-
table.get_row_index(
|
|
855
|
+
table.get_row_index(restore_event_id),
|
|
909
856
|
0,
|
|
910
857
|
)
|
|
911
858
|
except Exception:
|
|
@@ -913,12 +860,8 @@ class EventsTable(Container):
|
|
|
913
860
|
else:
|
|
914
861
|
table.cursor_coordinate = (0, 0)
|
|
915
862
|
|
|
916
|
-
#
|
|
917
|
-
|
|
918
|
-
pagination_widget = self.query_one("#pagination-info", Static)
|
|
919
|
-
pagination_widget.update(
|
|
920
|
-
f"[dim]Page {self.current_page}/{total_pages} ({self._total_events} total) │ (p) prev (n) next[/dim]"
|
|
921
|
-
)
|
|
863
|
+
# Clear saved cursor position after restore
|
|
864
|
+
self._saved_cursor_event_id = None
|
|
922
865
|
|
|
923
866
|
def _shorten_id(self, event_id: str) -> str:
|
|
924
867
|
"""Shorten an event ID for display."""
|