aline-ai 0.6.0__py3-none-any.whl → 0.6.1__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.0.dist-info → aline_ai-0.6.1.dist-info}/METADATA +1 -1
- {aline_ai-0.6.0.dist-info → aline_ai-0.6.1.dist-info}/RECORD +17 -12
- realign/__init__.py +1 -1
- realign/auth.py +21 -0
- realign/cli.py +44 -6
- realign/commands/auth.py +9 -0
- realign/dashboard/app.py +68 -6
- realign/dashboard/backends/__init__.py +6 -0
- realign/dashboard/backends/iterm2.py +599 -0
- realign/dashboard/backends/kitty.py +372 -0
- realign/dashboard/layout.py +320 -0
- realign/dashboard/terminal_backend.py +110 -0
- realign/dashboard/widgets/terminal_panel.py +566 -104
- {aline_ai-0.6.0.dist-info → aline_ai-0.6.1.dist-info}/WHEEL +0 -0
- {aline_ai-0.6.0.dist-info → aline_ai-0.6.1.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.6.0.dist-info → aline_ai-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.6.0.dist-info → aline_ai-0.6.1.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
aline_ai-0.6.
|
|
2
|
-
realign/__init__.py,sha256=
|
|
3
|
-
realign/auth.py,sha256=
|
|
1
|
+
aline_ai-0.6.1.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
|
|
2
|
+
realign/__init__.py,sha256=ohwpTX-XJAcf8rW1lcIJqXudQQ72HJvA9d0RsAJhURU,1623
|
|
3
|
+
realign/auth.py,sha256=d_1yvCwluN5iIrdgjtuSKpOYAksDzrzNgntKacLVJrw,16583
|
|
4
4
|
realign/claude_detector.py,sha256=ZLSJacMo6zzQclXByABKA70UNpstxqIv3fPGqdpA934,2792
|
|
5
|
-
realign/cli.py,sha256=
|
|
5
|
+
realign/cli.py,sha256=HIKpLIx7lwHV-Gd3cbdZVaOst784or6asQdAI-DFE80,43543
|
|
6
6
|
realign/codex_detector.py,sha256=N9ulgMgvTzDfXE4s4vLd6OoS0hT7R6h2bDFFXWa-2hE,4183
|
|
7
7
|
realign/config.py,sha256=d8HQ6v1xju6cjNGbR7LlfHaMyvFPcMDofhGbepxpQq8,11634
|
|
8
8
|
realign/context.py,sha256=8hzgNOg-7_eMW22wt7OM5H9IsmMveKXCv0epG7E0G7w,13917
|
|
@@ -34,7 +34,7 @@ realign/claude_hooks/user_prompt_submit_hook.py,sha256=WD-UavhBTueN2TPfnZrnPC7DF
|
|
|
34
34
|
realign/claude_hooks/user_prompt_submit_hook_installer.py,sha256=2xLF8yZcE7Iwib9gU-xCkA1NWxNH9Nc5CFKPYK7rtXw,5371
|
|
35
35
|
realign/commands/__init__.py,sha256=sx_ck55oxaoiF4N3LugG0ZXwonUDxeEZ5uHbBKCC7K8,89
|
|
36
36
|
realign/commands/add.py,sha256=njZgg3paUmOw-sb-sWkXr_eUaf5bD-hBEiRectaphPs,24332
|
|
37
|
-
realign/commands/auth.py,sha256=
|
|
37
|
+
realign/commands/auth.py,sha256=QrPukpP-ogYEDSwztV0NOYI-HDgn5fPxlCQ1-e2n7gU,11082
|
|
38
38
|
realign/commands/config.py,sha256=nYnu_h2pk7GODcrzrV04K51D-s7v06FlRXHJ0HJ-gvU,6732
|
|
39
39
|
realign/commands/context.py,sha256=pM2KfZHVkB-ou4nBhFvKSwnYliLBzwN3zerLyBAbhfE,7095
|
|
40
40
|
realign/commands/export_shares.py,sha256=g2SxlPEb7FPMarjTZcoZntviZo5JdqEe-M28vggc2-s,136601
|
|
@@ -46,8 +46,13 @@ realign/commands/upgrade.py,sha256=L3PLOUIN5qAQTbkfoVtSsIbbzEezA_xjjk9F1GMVfjw,1
|
|
|
46
46
|
realign/commands/watcher.py,sha256=rB3x2diC7scqghl7wLAeBNgMj4NLXUKbr0ou1_VVUIU,136292
|
|
47
47
|
realign/commands/worker.py,sha256=jTu7Pj60nTnn7SsH3oNCNnO6zl4TIFCJVNSC1OoQ_0o,23363
|
|
48
48
|
realign/dashboard/__init__.py,sha256=QZkHTsGityH8UkF8rmvA3xW7dMXNe0swEWr443qfgCM,128
|
|
49
|
-
realign/dashboard/app.py,sha256=
|
|
49
|
+
realign/dashboard/app.py,sha256=l4qbjQfnUJCzX2M0A5vPJ-fTX-Tz2Ykw4_cY0eMC1LE,16029
|
|
50
|
+
realign/dashboard/layout.py,sha256=sZxmFj6QTbkois9MHTvBEMMcnaRVehCDqugdbiFx10k,9072
|
|
51
|
+
realign/dashboard/terminal_backend.py,sha256=MlDfwtqhftyQK6jDNizQGFjAWIo5Bx2TDpSnP3MCZVM,3375
|
|
50
52
|
realign/dashboard/tmux_manager.py,sha256=Vt_30WNtDg7c_9SEh8xdDtBLJ8kNq6bGSPh5r3VXpg0,26276
|
|
53
|
+
realign/dashboard/backends/__init__.py,sha256=POROX7YKtukYZcLB1pi_kO0sSEpuO3y-hwmF3WIN1Kk,163
|
|
54
|
+
realign/dashboard/backends/iterm2.py,sha256=XYYJT5lrrp4pW_MyEqPZYkRI0qyKUwJlezwMidgnsHc,21390
|
|
55
|
+
realign/dashboard/backends/kitty.py,sha256=5jdkR1f2PwB8a4SnS3EG6uOQ2XU-PB7-cpKBfIJq3hU,12066
|
|
51
56
|
realign/dashboard/screens/__init__.py,sha256=US6sAmQs5VVkH2tFkH_z0WDT4H8cVhLL-JckfSR1yQY,446
|
|
52
57
|
realign/dashboard/screens/create_agent.py,sha256=lpcT1zLq_p02codtHTE8KdbEzCEaNLnk1lqU3QLcXCg,10057
|
|
53
58
|
realign/dashboard/screens/create_event.py,sha256=oiQY1zKpUYnQU-5fQLeuZH9BV5NClE5B5XZIVBYG5A8,5506
|
|
@@ -63,7 +68,7 @@ realign/dashboard/widgets/header.py,sha256=0HHCFXX7F3C6HII-WDwOJwWkJrajmKPWmdoMW
|
|
|
63
68
|
realign/dashboard/widgets/openable_table.py,sha256=GeJPDEYp0kRHShqvmPMzAePpYXRZHUNqcWNnxqsqxjA,1963
|
|
64
69
|
realign/dashboard/widgets/search_panel.py,sha256=ZNJDfwDSxUFnCeltYQYsQsPJ6t4HDeNWpENoTOoBdVM,8951
|
|
65
70
|
realign/dashboard/widgets/sessions_table.py,sha256=GyaWzvt-elKx1iE2YR94CBNPyjqM8B7g0j7zsukiXi0,36517
|
|
66
|
-
realign/dashboard/widgets/terminal_panel.py,sha256=
|
|
71
|
+
realign/dashboard/widgets/terminal_panel.py,sha256=7VrLN_Wsk98O0rojckrqJ_k5TGM47hP4UqXFvuRTvRs,45097
|
|
67
72
|
realign/dashboard/widgets/watcher_panel.py,sha256=O_mdDacgc87xA-5KEfta53Ik_Xsk_B2OfwenMOTtGw8,19722
|
|
68
73
|
realign/dashboard/widgets/worker_panel.py,sha256=F_jKWABuCNmjQgeeuCr4KnFRKdY4CLTNcEXMYwsNaSk,18691
|
|
69
74
|
realign/db/__init__.py,sha256=65LsNdsq_rkwNC1eg1OAr3HC0ORXtelOh0I8MhNGr-g,3288
|
|
@@ -91,8 +96,8 @@ realign/triggers/next_turn_trigger.py,sha256=BpP0PWn4mU1MZd6mv89jWcjs8Jtv0zEWapW
|
|
|
91
96
|
realign/triggers/registry.py,sha256=cb-AVLbYB2pqwfWL3q1DQxLv4kOw7g7m-GshTdfFESc,3827
|
|
92
97
|
realign/triggers/turn_status.py,sha256=wAZEhXDAmDoX5F-ohWfSnZZ0eA6DAJ9svSPiSv_f6sg,6041
|
|
93
98
|
realign/triggers/turn_summary.py,sha256=f3hEUshgv9skJ9AbfWpoYs417lsv_HK2A_vpPjgryO4,4467
|
|
94
|
-
aline_ai-0.6.
|
|
95
|
-
aline_ai-0.6.
|
|
96
|
-
aline_ai-0.6.
|
|
97
|
-
aline_ai-0.6.
|
|
98
|
-
aline_ai-0.6.
|
|
99
|
+
aline_ai-0.6.1.dist-info/METADATA,sha256=ftCsc3aI1fjTZhCJd8rE_7-teyrN8ARYIyEmDGMtJdg,1597
|
|
100
|
+
aline_ai-0.6.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
101
|
+
aline_ai-0.6.1.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
|
|
102
|
+
aline_ai-0.6.1.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
|
|
103
|
+
aline_ai-0.6.1.dist-info/RECORD,,
|
realign/__init__.py
CHANGED
realign/auth.py
CHANGED
|
@@ -44,6 +44,7 @@ logger = setup_logger("realign.auth", "auth.log")
|
|
|
44
44
|
# Hardcoded Supabase/backend configuration
|
|
45
45
|
DEFAULT_AUTH_URL = "https://realign-server.vercel.app"
|
|
46
46
|
CLI_LOGIN_PATH = "/cli-login"
|
|
47
|
+
CLI_LOGOUT_PATH = "/cli-logout"
|
|
47
48
|
CLI_VALIDATE_PATH = "/api/auth/cli/validate"
|
|
48
49
|
CLI_REFRESH_PATH = "/api/auth/cli/refresh"
|
|
49
50
|
|
|
@@ -394,6 +395,26 @@ def open_login_page(callback_port: Optional[int] = None) -> str:
|
|
|
394
395
|
return login_url
|
|
395
396
|
|
|
396
397
|
|
|
398
|
+
def open_logout_page() -> str:
|
|
399
|
+
"""
|
|
400
|
+
Open the web logout page in browser to sign out from web session.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
The URL that was opened
|
|
404
|
+
"""
|
|
405
|
+
config = ReAlignConfig.load()
|
|
406
|
+
backend_url = config.share_backend_url or DEFAULT_AUTH_URL
|
|
407
|
+
logout_url = f"{backend_url}{CLI_LOGOUT_PATH}"
|
|
408
|
+
|
|
409
|
+
try:
|
|
410
|
+
webbrowser.open(logout_url)
|
|
411
|
+
logger.info(f"Opened logout page: {logout_url}")
|
|
412
|
+
except Exception as e:
|
|
413
|
+
logger.warning(f"Failed to open browser: {e}")
|
|
414
|
+
|
|
415
|
+
return logout_url
|
|
416
|
+
|
|
417
|
+
|
|
397
418
|
# Local callback server for automatic token receipt
|
|
398
419
|
_received_token: Optional[str] = None
|
|
399
420
|
_server_error: Optional[str] = None
|
realign/cli.py
CHANGED
|
@@ -61,13 +61,33 @@ def main(
|
|
|
61
61
|
raise typer.Exit(0)
|
|
62
62
|
|
|
63
63
|
# Launch the dashboard when no subcommand is provided
|
|
64
|
-
|
|
64
|
+
import os
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
terminal_mode = os.environ.get("ALINE_TERMINAL_MODE", "").strip().lower()
|
|
67
|
+
use_native_terminal = terminal_mode in {"native", "iterm2", "iterm", "kitty"}
|
|
68
|
+
|
|
69
|
+
right_pane_session_id = None
|
|
70
|
+
|
|
71
|
+
if not use_native_terminal:
|
|
72
|
+
# Only bootstrap tmux for tmux mode (default)
|
|
73
|
+
from .dashboard.tmux_manager import bootstrap_dashboard_into_tmux
|
|
74
|
+
|
|
75
|
+
bootstrap_dashboard_into_tmux()
|
|
76
|
+
elif terminal_mode in {"iterm2", "iterm"}:
|
|
77
|
+
# Set up split pane layout for iTerm2
|
|
78
|
+
try:
|
|
79
|
+
from .dashboard.backends.iterm2 import setup_split_pane_layout_sync
|
|
80
|
+
|
|
81
|
+
right_pane_session_id = setup_split_pane_layout_sync()
|
|
82
|
+
if right_pane_session_id:
|
|
83
|
+
# Store in environment for dashboard to pick up
|
|
84
|
+
os.environ["ALINE_ITERM2_RIGHT_PANE"] = right_pane_session_id
|
|
85
|
+
except Exception as e:
|
|
86
|
+
console.print(f"[yellow]Warning: Could not set up split pane: {e}[/yellow]")
|
|
67
87
|
|
|
68
88
|
from .dashboard.app import AlineDashboard
|
|
69
89
|
|
|
70
|
-
dashboard = AlineDashboard(dev_mode=dev)
|
|
90
|
+
dashboard = AlineDashboard(dev_mode=dev, use_native_terminal=use_native_terminal)
|
|
71
91
|
dashboard.run()
|
|
72
92
|
|
|
73
93
|
|
|
@@ -1100,7 +1120,6 @@ def dashboard(
|
|
|
1100
1120
|
if debug:
|
|
1101
1121
|
os.environ["REALIGN_LOG_LEVEL"] = "DEBUG"
|
|
1102
1122
|
|
|
1103
|
-
from .dashboard.tmux_manager import bootstrap_dashboard_into_tmux
|
|
1104
1123
|
from .logging_config import setup_logger
|
|
1105
1124
|
|
|
1106
1125
|
# Initialize logger before dashboard
|
|
@@ -1108,13 +1127,32 @@ def dashboard(
|
|
|
1108
1127
|
logger.info(f"Dashboard command invoked (dev={dev}, debug={debug})")
|
|
1109
1128
|
|
|
1110
1129
|
try:
|
|
1111
|
-
|
|
1130
|
+
# Check terminal mode
|
|
1131
|
+
terminal_mode = os.environ.get("ALINE_TERMINAL_MODE", "").strip().lower()
|
|
1132
|
+
use_native_terminal = terminal_mode in {"native", "iterm2", "iterm", "kitty"}
|
|
1133
|
+
|
|
1134
|
+
if not use_native_terminal:
|
|
1135
|
+
# Only bootstrap tmux for tmux mode (default)
|
|
1136
|
+
from .dashboard.tmux_manager import bootstrap_dashboard_into_tmux
|
|
1137
|
+
|
|
1138
|
+
bootstrap_dashboard_into_tmux()
|
|
1139
|
+
elif terminal_mode in {"iterm2", "iterm"}:
|
|
1140
|
+
# Set up split pane layout for iTerm2
|
|
1141
|
+
try:
|
|
1142
|
+
from .dashboard.backends.iterm2 import setup_split_pane_layout_sync
|
|
1143
|
+
|
|
1144
|
+
right_pane_session_id = setup_split_pane_layout_sync()
|
|
1145
|
+
if right_pane_session_id:
|
|
1146
|
+
os.environ["ALINE_ITERM2_RIGHT_PANE"] = right_pane_session_id
|
|
1147
|
+
logger.info(f"Set up split pane with right pane: {right_pane_session_id}")
|
|
1148
|
+
except Exception as e:
|
|
1149
|
+
logger.warning(f"Could not set up split pane: {e}")
|
|
1112
1150
|
|
|
1113
1151
|
from .dashboard.app import AlineDashboard
|
|
1114
1152
|
|
|
1115
1153
|
# Use dev flag from this command or inherit from parent context
|
|
1116
1154
|
dev_mode = dev or (ctx.obj.get("dev", False) if ctx.obj else False)
|
|
1117
|
-
dash = AlineDashboard(dev_mode=dev_mode)
|
|
1155
|
+
dash = AlineDashboard(dev_mode=dev_mode, use_native_terminal=use_native_terminal)
|
|
1118
1156
|
dash.run()
|
|
1119
1157
|
except Exception as e:
|
|
1120
1158
|
logger.error(f"Dashboard crashed: {e}\n{traceback.format_exc()}")
|
realign/commands/auth.py
CHANGED
|
@@ -25,6 +25,7 @@ from ..auth import (
|
|
|
25
25
|
save_credentials,
|
|
26
26
|
clear_credentials,
|
|
27
27
|
open_login_page,
|
|
28
|
+
open_logout_page,
|
|
28
29
|
validate_cli_token,
|
|
29
30
|
find_free_port,
|
|
30
31
|
start_callback_server,
|
|
@@ -207,6 +208,14 @@ def logout_command() -> int:
|
|
|
207
208
|
print("Error: Failed to clear credentials", file=sys.stderr)
|
|
208
209
|
return 1
|
|
209
210
|
|
|
211
|
+
# Open browser to sign out from web session
|
|
212
|
+
if console:
|
|
213
|
+
console.print("[dim]Signing out from web session...[/dim]")
|
|
214
|
+
else:
|
|
215
|
+
print("Signing out from web session...")
|
|
216
|
+
|
|
217
|
+
open_logout_page()
|
|
218
|
+
|
|
210
219
|
if console:
|
|
211
220
|
console.print(f"[green]Logged out successfully.[/green]")
|
|
212
221
|
console.print(f"Cleared credentials for: {email}")
|
realign/dashboard/app.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Aline Dashboard - Main Application."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import subprocess
|
|
4
5
|
import sys
|
|
5
6
|
import time
|
|
@@ -22,6 +23,9 @@ from .widgets import (
|
|
|
22
23
|
TerminalPanel,
|
|
23
24
|
)
|
|
24
25
|
|
|
26
|
+
# Environment variable to control terminal mode
|
|
27
|
+
ENV_TERMINAL_MODE = "ALINE_TERMINAL_MODE"
|
|
28
|
+
|
|
25
29
|
# Set up dashboard logger - logs to ~/.aline/.logs/dashboard.log
|
|
26
30
|
logger = setup_logger("realign.dashboard", "dashboard.log")
|
|
27
31
|
|
|
@@ -74,15 +78,31 @@ class AlineDashboard(App):
|
|
|
74
78
|
|
|
75
79
|
_quit_confirm_window_s: float = 1.2
|
|
76
80
|
|
|
77
|
-
def __init__(self, dev_mode: bool = False):
|
|
81
|
+
def __init__(self, dev_mode: bool = False, use_native_terminal: bool | None = None):
|
|
78
82
|
"""Initialize the dashboard.
|
|
79
83
|
|
|
80
84
|
Args:
|
|
81
85
|
dev_mode: If True, shows developer tabs (Watcher, Worker).
|
|
86
|
+
use_native_terminal: If True, use native terminal backend (iTerm2/Kitty).
|
|
87
|
+
If False, use tmux.
|
|
88
|
+
If None (default), auto-detect from ALINE_TERMINAL_MODE env var.
|
|
82
89
|
"""
|
|
83
90
|
super().__init__()
|
|
84
91
|
self.dev_mode = dev_mode
|
|
85
|
-
|
|
92
|
+
self.use_native_terminal = use_native_terminal
|
|
93
|
+
self._native_terminal_mode = self._detect_native_mode()
|
|
94
|
+
logger.info(
|
|
95
|
+
f"AlineDashboard initialized (dev_mode={dev_mode}, "
|
|
96
|
+
f"native_terminal={self._native_terminal_mode})"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def _detect_native_mode(self) -> bool:
|
|
100
|
+
"""Detect if native terminal mode should be used."""
|
|
101
|
+
if self.use_native_terminal is not None:
|
|
102
|
+
return self.use_native_terminal
|
|
103
|
+
|
|
104
|
+
mode = os.environ.get(ENV_TERMINAL_MODE, "").strip().lower()
|
|
105
|
+
return mode in {"native", "iterm2", "iterm", "kitty"}
|
|
86
106
|
|
|
87
107
|
def compose(self) -> ComposeResult:
|
|
88
108
|
"""Compose the dashboard layout."""
|
|
@@ -92,7 +112,7 @@ class AlineDashboard(App):
|
|
|
92
112
|
tab_ids = self._tab_ids()
|
|
93
113
|
with TabbedContent(initial=tab_ids[0] if tab_ids else "terminal"):
|
|
94
114
|
with TabPane("Agents", id="terminal"):
|
|
95
|
-
yield TerminalPanel()
|
|
115
|
+
yield TerminalPanel(use_native_terminal=self.use_native_terminal)
|
|
96
116
|
if self.dev_mode:
|
|
97
117
|
with TabPane("Watcher", id="watcher"):
|
|
98
118
|
yield WatcherPanel()
|
|
@@ -125,11 +145,47 @@ class AlineDashboard(App):
|
|
|
125
145
|
# Check for system theme changes every 2 seconds
|
|
126
146
|
self.set_interval(2, self._sync_theme)
|
|
127
147
|
self._quit_confirm_deadline: float | None = None
|
|
148
|
+
|
|
149
|
+
# Set up side-by-side layout for native terminal mode
|
|
150
|
+
if self._native_terminal_mode:
|
|
151
|
+
self._setup_native_terminal_layout()
|
|
152
|
+
|
|
128
153
|
logger.info("on_mount() completed successfully")
|
|
129
154
|
except Exception as e:
|
|
130
155
|
logger.error(f"on_mount() failed: {e}\n{traceback.format_exc()}")
|
|
131
156
|
raise
|
|
132
157
|
|
|
158
|
+
def _setup_native_terminal_layout(self) -> None:
|
|
159
|
+
"""Set up side-by-side layout for Dashboard and native terminal."""
|
|
160
|
+
# Skip if using iTerm2 split pane mode (already set up by CLI)
|
|
161
|
+
if os.environ.get("ALINE_ITERM2_RIGHT_PANE"):
|
|
162
|
+
logger.info("Using iTerm2 split pane mode, skipping window layout")
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
from .layout import setup_side_by_side_layout
|
|
167
|
+
|
|
168
|
+
# Determine the target terminal app
|
|
169
|
+
mode = os.environ.get(ENV_TERMINAL_MODE, "").strip().lower()
|
|
170
|
+
if mode == "kitty":
|
|
171
|
+
terminal_app = "Kitty"
|
|
172
|
+
else:
|
|
173
|
+
terminal_app = "iTerm2"
|
|
174
|
+
|
|
175
|
+
# Set up side-by-side layout (Dashboard on left, terminal on right)
|
|
176
|
+
success = setup_side_by_side_layout(
|
|
177
|
+
terminal_app=terminal_app,
|
|
178
|
+
dashboard_on_left=True,
|
|
179
|
+
dashboard_width_percent=40, # Dashboard takes 40%, terminal takes 60%
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if success:
|
|
183
|
+
logger.info(f"Set up side-by-side layout with {terminal_app}")
|
|
184
|
+
else:
|
|
185
|
+
logger.warning("Failed to set up side-by-side layout")
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.warning(f"Could not set up native terminal layout: {e}")
|
|
188
|
+
|
|
133
189
|
def _sync_theme(self) -> None:
|
|
134
190
|
"""Sync app theme with system theme."""
|
|
135
191
|
target_theme = "textual-dark" if _detect_system_dark_mode() else "textual-light"
|
|
@@ -357,11 +413,17 @@ class AlineDashboard(App):
|
|
|
357
413
|
self.notify(f"Loaded {what} into {context_id}", title="Load Context", timeout=3)
|
|
358
414
|
|
|
359
415
|
|
|
360
|
-
def run_dashboard() -> None:
|
|
361
|
-
"""Run the Aline Dashboard.
|
|
416
|
+
def run_dashboard(use_native_terminal: bool | None = None) -> None:
|
|
417
|
+
"""Run the Aline Dashboard.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
use_native_terminal: If True, use native terminal backend (iTerm2/Kitty).
|
|
421
|
+
If False, use tmux.
|
|
422
|
+
If None (default), auto-detect from ALINE_TERMINAL_MODE env var.
|
|
423
|
+
"""
|
|
362
424
|
logger.info("Starting Aline Dashboard")
|
|
363
425
|
try:
|
|
364
|
-
app = AlineDashboard()
|
|
426
|
+
app = AlineDashboard(use_native_terminal=use_native_terminal)
|
|
365
427
|
app.run()
|
|
366
428
|
logger.info("Aline Dashboard exited normally")
|
|
367
429
|
except Exception as e:
|