feat: Dashboard improvements and configuration updates

Dashboard:
- Enhanced terminal components (ClaudeTerminal, GlobalClaudeTerminal)
- Improved MarkdownRenderer for better documentation display
- Updated convergence plots (ConvergencePlot, PlotlyConvergencePlot)
- Refined Home, Analysis, Dashboard, Setup, Results pages
- Added StudyContext improvements
- Updated vite.config for better dev experience

Configuration:
- Updated CLAUDE.md with latest instructions
- Enhanced launch_dashboard.py
- Updated config.py settings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-20 13:47:05 -05:00
parent 1612991d0d
commit 7c700c4606
19 changed files with 478 additions and 173 deletions

View File

@@ -20,9 +20,43 @@ router = APIRouter()
_terminal_sessions: dict = {}
# Path to Atomizer root (for loading prompts)
ATOMIZER_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)))
# Go up 5 levels: terminal.py -> routes -> api -> backend -> atomizer-dashboard -> Atomizer
_file_path = os.path.abspath(__file__)
ATOMIZER_ROOT = os.path.normpath(os.path.dirname(os.path.dirname(os.path.dirname(
os.path.dirname(os.path.dirname(_file_path))
))))
STUDIES_DIR = os.path.join(ATOMIZER_ROOT, "studies")
# Debug: print the resolved path at module load
print(f"[Terminal] ATOMIZER_ROOT resolved to: {ATOMIZER_ROOT}")
def resolve_study_path(study_id: str) -> str:
"""Find study folder by scanning topic directories.
Returns relative path from ATOMIZER_ROOT (e.g., 'studies/M1_Mirror/m1_mirror_adaptive_V14').
"""
# First check direct path (flat structure)
direct_path = os.path.join(STUDIES_DIR, study_id)
if os.path.isdir(direct_path):
setup_dir = os.path.join(direct_path, "1_setup")
config_file = os.path.join(direct_path, "optimization_config.json")
if os.path.exists(setup_dir) or os.path.exists(config_file):
return f"studies/{study_id}"
# Scan topic folders for nested structure
if os.path.isdir(STUDIES_DIR):
for topic_name in os.listdir(STUDIES_DIR):
topic_path = os.path.join(STUDIES_DIR, topic_name)
if os.path.isdir(topic_path) and not topic_name.startswith('.'):
study_path = os.path.join(topic_path, study_id)
if os.path.isdir(study_path):
setup_dir = os.path.join(study_path, "1_setup")
config_file = os.path.join(study_path, "optimization_config.json")
if os.path.exists(setup_dir) or os.path.exists(config_file):
return f"studies/{topic_name}/{study_id}"
# Fallback to flat path
return f"studies/{study_id}"
def get_session_prompt(study_name: str = None) -> str:
@@ -54,19 +88,21 @@ def get_session_prompt(study_name: str = None) -> str:
]
if study_name:
# Resolve actual study path (handles nested folder structure)
study_path = resolve_study_path(study_name)
prompt_lines.extend([
f"## Current Study: `{study_name}`",
"",
f"**Directory**: `studies/{study_name}/`",
f"**Directory**: `{study_path}/`",
"",
"Key files:",
f"- `studies/{study_name}/1_setup/optimization_config.json` - Configuration",
f"- `studies/{study_name}/2_results/study.db` - Optuna database",
f"- `studies/{study_name}/README.md` - Study documentation",
f"- `{study_path}/1_setup/optimization_config.json` - Configuration",
f"- `{study_path}/3_results/study.db` - Optuna database",
f"- `{study_path}/README.md` - Study documentation",
"",
"Quick status check:",
"```bash",
f"python -c \"import optuna; s=optuna.load_study('{study_name}', 'sqlite:///studies/{study_name}/2_results/study.db'); print(f'Trials: {{len(s.trials)}}, Best: {{s.best_value}}')\"",
f"python -c \"import optuna; s=optuna.load_study('{study_name}', 'sqlite:///{study_path}/3_results/study.db'); print(f'Trials: {{len(s.trials)}}, Best: {{s.best_value}}')\"",
"```",
"",
])
@@ -99,8 +135,10 @@ def get_session_prompt(study_name: str = None) -> str:
try:
from winpty import PtyProcess
HAS_WINPTY = True
except ImportError:
print("[Terminal] winpty is available")
except ImportError as e:
HAS_WINPTY = False
print(f"[Terminal] winpty not available: {e}")
class TerminalSession:
@@ -120,6 +158,15 @@ class TerminalSession:
self.websocket = websocket
self._running = True
# Validate working directory exists
if not os.path.isdir(self.working_dir):
await self.websocket.send_json({
"type": "error",
"message": f"Working directory does not exist: {self.working_dir}"
})
self._running = False
return
try:
if self._use_winpty:
# Use winpty for proper PTY on Windows
@@ -143,7 +190,8 @@ class TerminalSession:
)
elif sys.platform == "win32":
# Fallback: Windows without winpty - use subprocess
# Run claude with --dangerously-skip-permissions for non-interactive mode
import shutil
claude_cmd = shutil.which("claude") or "claude"
self.process = subprocess.Popen(
["cmd.exe", "/k", claude_cmd],
stdin=subprocess.PIPE,
@@ -157,6 +205,8 @@ class TerminalSession:
else:
# On Unix, use pty
import pty
import shutil
claude_cmd = shutil.which("claude") or "claude"
master_fd, slave_fd = pty.openpty()
self.process = subprocess.Popen(
[claude_cmd],
@@ -467,6 +517,9 @@ async def get_context(study_id: str = None):
"""
prompt = get_session_prompt(study_id)
# Resolve study path for nested folder structure
study_path = resolve_study_path(study_id) if study_id else None
return {
"study_id": study_id,
"prompt": prompt,
@@ -476,8 +529,8 @@ async def get_context(study_id: str = None):
".claude/skills/02_CONTEXT_LOADER.md",
],
"study_files": [
f"studies/{study_id}/1_setup/optimization_config.json",
f"studies/{study_id}/2_results/study.db",
f"studies/{study_id}/README.md",
] if study_id else []
f"{study_path}/1_setup/optimization_config.json",
f"{study_path}/3_results/study.db",
f"{study_path}/README.md",
] if study_path else []
}