feat(workspace): implement contextual auto-switch layout based on MMA active tier
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
def verify_phase_4():
|
||||||
|
print("Verifying Phase 4: Contextual Auto-Switch...")
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
["uv", "run", "pytest", "tests/test_auto_switch_sim.py"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("Phase 4 verification PASSED.")
|
||||||
|
else:
|
||||||
|
print("Phase 4 verification FAILED.")
|
||||||
|
print(result.stdout)
|
||||||
|
print(result.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
verify_phase_4()
|
||||||
+15
-1
@@ -406,7 +406,9 @@ class AppController:
|
|||||||
'text_viewer_title': 'text_viewer_title',
|
'text_viewer_title': 'text_viewer_title',
|
||||||
'text_viewer_type': 'text_viewer_type',
|
'text_viewer_type': 'text_viewer_type',
|
||||||
'disc_entries': 'disc_entries',
|
'disc_entries': 'disc_entries',
|
||||||
'ui_file_paths': 'ui_file_paths'
|
'ui_file_paths': 'ui_file_paths',
|
||||||
|
'ui_auto_switch_layout': 'ui_auto_switch_layout',
|
||||||
|
'ui_tier_layout_bindings': 'ui_tier_layout_bindings'
|
||||||
}
|
}
|
||||||
self._gettable_fields = dict(self._settable_fields)
|
self._gettable_fields = dict(self._settable_fields)
|
||||||
self._gettable_fields.update({
|
self._gettable_fields.update({
|
||||||
@@ -800,8 +802,18 @@ class AppController:
|
|||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
self.mma_status = p.get("status", self.mma_status)
|
self.mma_status = p.get("status", self.mma_status)
|
||||||
|
|
||||||
|
old_tier = self.active_tier
|
||||||
self.active_tier = p.get("active_tier", self.active_tier)
|
self.active_tier = p.get("active_tier", self.active_tier)
|
||||||
|
|
||||||
|
if getattr(self, "ui_auto_switch_layout", False) and self.active_tier and self.active_tier != old_tier:
|
||||||
|
for tier_prefix in ["Tier 1", "Tier 2", "Tier 3", "Tier 4"]:
|
||||||
|
if self.active_tier.startswith(tier_prefix):
|
||||||
|
bound_profile = getattr(self, "ui_tier_layout_bindings", {}).get(tier_prefix)
|
||||||
|
if bound_profile:
|
||||||
|
self._cb_load_workspace_profile(bound_profile)
|
||||||
|
break
|
||||||
|
|
||||||
# Preserve existing model/provider config if not explicitly in payload
|
# Preserve existing model/provider config if not explicitly in payload
|
||||||
new_usage = p.get("tier_usage", {})
|
new_usage = p.get("tier_usage", {})
|
||||||
for tier, data in new_usage.items():
|
for tier, data in new_usage.items():
|
||||||
@@ -1121,6 +1133,8 @@ class AppController:
|
|||||||
self.ui_project_preset_name = proj_meta.get("active_preset")
|
self.ui_project_preset_name = proj_meta.get("active_preset")
|
||||||
|
|
||||||
gui_cfg = self.config.get("gui", {})
|
gui_cfg = self.config.get("gui", {})
|
||||||
|
self.ui_auto_switch_layout = gui_cfg.get("auto_switch_layout", False)
|
||||||
|
self.ui_tier_layout_bindings = gui_cfg.get("tier_layout_bindings", {"Tier 1": "", "Tier 2": "", "Tier 3": "", "Tier 4": ""})
|
||||||
from src import bg_shader
|
from src import bg_shader
|
||||||
bg_shader.get_bg().enabled = gui_cfg.get("bg_shader_enabled", False)
|
bg_shader.get_bg().enabled = gui_cfg.get("bg_shader_enabled", False)
|
||||||
|
|
||||||
|
|||||||
@@ -972,6 +972,20 @@ class App:
|
|||||||
if imgui.begin_tab_item("External Tools")[0]:
|
if imgui.begin_tab_item("External Tools")[0]:
|
||||||
self._render_external_tools_panel()
|
self._render_external_tools_panel()
|
||||||
imgui.end_tab_item()
|
imgui.end_tab_item()
|
||||||
|
if imgui.begin_tab_item("Workspace Layouts")[0]:
|
||||||
|
imgui.text("Experimental: Auto-switch layout by Tier")
|
||||||
|
ch, self.controller.ui_auto_switch_layout = imgui.checkbox("Enable Auto-Switch", self.controller.ui_auto_switch_layout)
|
||||||
|
if self.controller.ui_auto_switch_layout:
|
||||||
|
imgui.separator()
|
||||||
|
imgui.text("Tier Bindings (select profile for each tier)")
|
||||||
|
profiles = [""] + [p.name for p in self.controller.workspace_profiles.values()]
|
||||||
|
for t in ["Tier 1", "Tier 2", "Tier 3", "Tier 4"]:
|
||||||
|
curr = self.controller.ui_tier_layout_bindings.get(t, "")
|
||||||
|
idx = profiles.index(curr) if curr in profiles else 0
|
||||||
|
ch_combo, new_idx = imgui.combo(t, idx, profiles)
|
||||||
|
if ch_combo:
|
||||||
|
self.controller.ui_tier_layout_bindings[t] = profiles[new_idx]
|
||||||
|
imgui.end_tab_item()
|
||||||
imgui.end_tab_bar()
|
imgui.end_tab_bar()
|
||||||
imgui.end()
|
imgui.end()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import pytest
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
|
||||||
|
|
||||||
|
from src import api_hook_client
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_auto_switch_sim(live_gui):
|
||||||
|
client = api_hook_client.ApiHookClient()
|
||||||
|
assert client.wait_for_server(timeout=15), "Hook server did not start"
|
||||||
|
|
||||||
|
# Reset layout and save a test profile for Tier 3
|
||||||
|
client.set_value('show_windows', {'Diagnostics': True})
|
||||||
|
client.push_event('custom_callback', {'callback': 'save_workspace_profile', 'args': ['Tier3Profile', 'project']})
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Reset layout to something else
|
||||||
|
client.set_value('show_windows', {'Diagnostics': False})
|
||||||
|
|
||||||
|
# Enable auto switch and bind
|
||||||
|
client.set_value('ui_auto_switch_layout', True)
|
||||||
|
client.set_value('ui_tier_layout_bindings', {'Tier 1': '', 'Tier 2': '', 'Tier 3': 'Tier3Profile', 'Tier 4': ''})
|
||||||
|
|
||||||
|
# Send mma_state_update event to trigger Tier 2
|
||||||
|
# Since we can't send raw asyncio events easily via ApiHookClient without a dedicated endpoint,
|
||||||
|
# we can simulate it by setting the active_tier via the Hook API if it triggers the logic,
|
||||||
|
# OR we can just inject an event into the app's event queue via custom_callback.
|
||||||
|
|
||||||
|
def trigger_tier(tier):
|
||||||
|
# Inject mma_state_update task directly via hook API
|
||||||
|
client.push_event("mma_state_update", {"status": "running", "active_tier": tier})
|
||||||
|
|
||||||
|
# First Tier 2
|
||||||
|
trigger_tier('Tier 2 (Tech Lead)')
|
||||||
|
time.sleep(1)
|
||||||
|
assert client.get_value('show_windows').get('Diagnostics', False) == False
|
||||||
|
|
||||||
|
# Then Tier 3
|
||||||
|
trigger_tier('Tier 3 (Worker): task-1')
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
assert client.get_value('show_windows').get('Diagnostics', False) == True
|
||||||
|
|
||||||
|
print("Contextual auto-switch simulation PASSED.")
|
||||||
Reference in New Issue
Block a user