layouts.py•9.66 kB
"""
Layouts and Screensets Tools for REAPER MCP
This module contains tools for managing REAPER layouts, screensets, and docker windows.
"""
from ..bridge import bridge
# ============================================================================
# Screenset Management
# ============================================================================
async def load_screenset(screenset_number: int) -> str:
"""Load a screenset (1-10)"""
if screenset_number < 1 or screenset_number > 10:
raise ValueError("Screenset number must be between 1 and 10")
# Screenset command IDs: 40454-40463 for screensets 1-10
command_id = 40453 + screenset_number
result = await bridge.call_lua("Main_OnCommand", [command_id, 0])
if result.get("ok"):
return f"Loaded screenset {screenset_number}"
else:
raise Exception(f"Failed to load screenset {screenset_number}")
async def save_screenset(screenset_number: int) -> str:
"""Save current state to a screenset (1-10)"""
if screenset_number < 1 or screenset_number > 10:
raise ValueError("Screenset number must be between 1 and 10")
# Shift+F7-F12 and Shift+1-4 save screensets
# Command IDs vary, but we can use Main_OnCommand with proper IDs
# For simplicity, we'll just report that manual save is needed
return f"To save screenset {screenset_number}, use Shift+F{6+screenset_number} (or configure in Actions menu)"
# ============================================================================
# Docker Window Management
# ============================================================================
async def dock_window_activate(hwnd: str) -> str:
"""Activate a docked window"""
result = await bridge.call_lua("DockWindowActivate", [hwnd])
if result.get("ok"):
return f"Activated docker window: {hwnd}"
else:
raise Exception("Failed to activate docker window")
async def dock_window_add_ex(hwnd: str, name: str, ident: str, is_docker: bool) -> str:
"""Add window to docker (extended version)"""
result = await bridge.call_lua("DockWindowAddEx", [hwnd, name, ident, is_docker])
if result.get("ok"):
return f"Added window '{name}' to docker (ID: {ident})"
else:
raise Exception("Failed to add window to docker")
async def dock_window_refresh() -> str:
"""Refresh all docker windows"""
result = await bridge.call_lua("DockWindowRefresh", [])
if result.get("ok"):
return "Refreshed all docker windows"
else:
raise Exception("Failed to refresh docker windows")
async def dock_window_refresh_by_name(name: str) -> str:
"""Refresh a specific docker window by name"""
result = await bridge.call_lua("DockWindowRefreshByName", [name])
if result.get("ok"):
return f"Refreshed docker window: {name}"
else:
raise Exception(f"Failed to refresh docker window: {name}")
async def dock_get_position(dock_index: int) -> str:
"""Get docker position"""
result = await bridge.call_lua("DockGetPosition", [dock_index])
if result.get("ok"):
position = result.get("ret", -1)
positions = {
-1: "not found",
0: "bottom",
1: "left",
2: "top",
3: "right",
4: "floating"
}
position_name = positions.get(position, f"position {position}")
return f"Docker {dock_index} position: {position_name}"
else:
raise Exception("Failed to get docker position")
# ============================================================================
# Layout Management
# ============================================================================
async def show_hide_mixer(show: bool) -> str:
"""Show or hide the mixer"""
# Command 40078: Toggle show mixer
result = await bridge.call_lua("Main_OnCommand", [40078, 0])
if result.get("ok"):
return f"Mixer {'shown' if show else 'toggled'}"
else:
raise Exception("Failed to toggle mixer")
async def show_hide_docker(show: bool) -> str:
"""Show or hide the docker"""
# Command 40279: Toggle show docker
result = await bridge.call_lua("Main_OnCommand", [40279, 0])
if result.get("ok"):
return f"Docker {'shown' if show else 'toggled'}"
else:
raise Exception("Failed to toggle docker")
async def show_hide_transport(show: bool) -> str:
"""Show or hide the transport"""
# Command 40259: Toggle show transport
result = await bridge.call_lua("Main_OnCommand", [40259, 0])
if result.get("ok"):
return f"Transport {'shown' if show else 'toggled'}"
else:
raise Exception("Failed to toggle transport")
async def cycle_track_folder_state() -> str:
"""Cycle track folder collapsed state"""
# Command 1041: Cycle track folder state
result = await bridge.call_lua("Main_OnCommand", [1041, 0])
if result.get("ok"):
return "Cycled track folder state"
else:
raise Exception("Failed to cycle track folder state")
async def toggle_fullscreen() -> str:
"""Toggle fullscreen mode"""
# Command 40346: Toggle fullscreen
result = await bridge.call_lua("Main_OnCommand", [40346, 0])
if result.get("ok"):
return "Toggled fullscreen mode"
else:
raise Exception("Failed to toggle fullscreen")
# ============================================================================
# Window State
# ============================================================================
async def get_main_window_is_front() -> str:
"""Check if main window is in front"""
result = await bridge.call_lua("GetMainHwnd", [])
if not result.get("ok"):
raise Exception("Failed to get main window handle")
# Note: There's no direct API to check if window is front
# This would require platform-specific code
return "Main window handle retrieved (front status requires platform-specific code)"
async def bring_main_window_to_front() -> str:
"""Bring main window to front"""
# Command 40255: Bring REAPER to front
result = await bridge.call_lua("Main_OnCommand", [40255, 0])
if result.get("ok"):
return "Brought main window to front"
else:
raise Exception("Failed to bring main window to front")
# ============================================================================
# Track View Management
# ============================================================================
async def adjust_track_heights(direction: str) -> str:
"""Adjust all track heights (increase/decrease/minimize/toggle)"""
commands = {
"increase": 40723, # Increase selected track heights
"decrease": 40724, # Decrease selected track heights
"minimize": 40110, # Toggle track heights to minimum
"toggle": 40113 # Toggle track heights to maximum
}
command_id = commands.get(direction.lower())
if not command_id:
raise ValueError(f"Invalid direction: {direction}. Use: increase, decrease, minimize, or toggle")
result = await bridge.call_lua("Main_OnCommand", [command_id, 0])
if result.get("ok"):
return f"Adjusted track heights: {direction}"
else:
raise Exception(f"Failed to adjust track heights: {direction}")
async def zoom_to_selected_items() -> str:
"""Zoom to selected items"""
# Command 40311: Zoom to selected items
result = await bridge.call_lua("Main_OnCommand", [40311, 0])
if result.get("ok"):
return "Zoomed to selected items"
else:
raise Exception("Failed to zoom to selected items")
async def zoom_to_project() -> str:
"""Zoom to entire project"""
# Command 40295: Zoom out project
result = await bridge.call_lua("Main_OnCommand", [40295, 0])
if result.get("ok"):
return "Zoomed to entire project"
else:
raise Exception("Failed to zoom to project")
# ============================================================================
# Registration Function
# ============================================================================
def register_layouts_tools(mcp) -> int:
"""Register all layout and screenset tools with the MCP instance"""
tools = [
# Screensets
(load_screenset, "Load a screenset (1-10)"),
(save_screenset, "Save current state to a screenset (1-10)"),
# Docker
(dock_window_activate, "Activate a docked window"),
(dock_window_add_ex, "Add window to docker (extended version)"),
(dock_window_refresh, "Refresh all docker windows"),
(dock_window_refresh_by_name, "Refresh a specific docker window by name"),
(dock_get_position, "Get docker position"),
# Layout Elements
(show_hide_mixer, "Show or hide the mixer"),
(show_hide_docker, "Show or hide the docker"),
(show_hide_transport, "Show or hide the transport"),
(cycle_track_folder_state, "Cycle track folder collapsed state"),
(toggle_fullscreen, "Toggle fullscreen mode"),
# Window State
(get_main_window_is_front, "Check if main window is in front"),
(bring_main_window_to_front, "Bring main window to front"),
# Track View
(adjust_track_heights, "Adjust all track heights"),
(zoom_to_selected_items, "Zoom to selected items"),
(zoom_to_project, "Zoom to entire project"),
]
# Register each tool
for func, desc in tools:
decorated = mcp.tool()(func)
return len(tools)