refactor(dashboard): change SpawnModal from overlay modal to dropdown

Position the spawn modal directly under the 'New Agent' button without a
blur overlay. Uses click-outside dismissal and absolute positioning.
Reduces visual disruption for quick agent spawning.
This commit is contained in:
teernisse
2026-02-26 17:10:41 -05:00
parent 7a9d290cb9
commit baa712ba15
42 changed files with 86 additions and 61 deletions

Binary file not shown.

View File

@@ -325,8 +325,8 @@ class TestDismissSession(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmpdir:
sessions_dir = Path(tmpdir)
sessions_dir.mkdir(exist_ok=True)
# Create a file that should NOT be deleted
secret_file = Path(tmpdir).parent / "secret.json"
# Create a file that should NOT be deleted (unused - documents test intent)
_secret_file = Path(tmpdir).parent / "secret.json"
handler = DummyControlHandler()
with patch.object(control, "SESSIONS_DIR", sessions_dir):

View File

@@ -7,8 +7,7 @@ import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch, MagicMock
import io
from unittest.mock import patch
from amc_server.mixins.conversation import ConversationMixin
from amc_server.mixins.parsing import SessionParsingMixin
@@ -72,8 +71,8 @@ class TestServeEvents(unittest.TestCase):
def test_path_traversal_sanitized(self):
with tempfile.TemporaryDirectory() as tmpdir:
events_dir = Path(tmpdir)
# Create a file that path traversal might try to access
secret_file = Path(tmpdir).parent / "secret.jsonl"
# Create a file that path traversal might try to access (unused - documents intent)
_secret_file = Path(tmpdir).parent / "secret.jsonl"
with patch("amc_server.mixins.conversation.EVENTS_DIR", events_dir):
# Try path traversal

View File

@@ -6,7 +6,6 @@ Edge cases are prioritized over happy paths.
import json
import os
import sys
import tempfile
import types
import unittest
@@ -193,7 +192,7 @@ class TestAtomicWrite(unittest.TestCase):
path.write_text('{"original": "data"}')
# Mock os.replace to fail after the temp file is written
original_replace = os.replace
_original_replace = os.replace # noqa: F841 - documents test setup
def failing_replace(src, dst):
raise PermissionError("Simulated failure")

View File

@@ -128,8 +128,8 @@ class TestServeDashboardFile(unittest.TestCase):
handler._json_error = capture_error
with tempfile.TemporaryDirectory() as tmpdir:
# Create a file outside the dashboard dir that shouldn't be accessible
secret = Path(tmpdir).parent / "secret.txt"
# Create a file outside the dashboard dir that shouldn't be accessible (unused - documents intent)
_secret = Path(tmpdir).parent / "secret.txt"
with patch("amc_server.mixins.http.DASHBOARD_DIR", Path(tmpdir)):
handler._serve_dashboard_file("../secret.txt")

View File

@@ -7,7 +7,7 @@ import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch, MagicMock
from unittest.mock import patch
from amc_server.mixins.parsing import SessionParsingMixin

View File

@@ -7,7 +7,7 @@ from pathlib import Path
from unittest.mock import MagicMock, patch
import amc_server.mixins.spawn as spawn_mod
from amc_server.mixins.spawn import SpawnMixin, load_projects_cache, _projects_cache, _sanitize_pane_name
from amc_server.mixins.spawn import SpawnMixin, load_projects_cache, _sanitize_pane_name
class DummySpawnHandler(SpawnMixin):
@@ -317,7 +317,7 @@ class TestRateLimiting(unittest.TestCase):
def test_rapid_spawn_same_project_rejected(self):
"""Spawning the same project within cooldown returns 429."""
from amc_server.context import _spawn_timestamps, SPAWN_COOLDOWN_SEC
from amc_server.context import _spawn_timestamps
_spawn_timestamps.clear()
# Pretend we just spawned this project
_spawn_timestamps['rapid-project'] = time.monotonic()

View File

@@ -4,7 +4,7 @@ import tempfile
import time
import unittest
from pathlib import Path
from unittest.mock import patch, MagicMock
from unittest.mock import patch
import amc_server.mixins.state as state_mod
from amc_server.mixins.state import StateMixin

View File

@@ -13,7 +13,7 @@ import tempfile
import types
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch, call
from unittest.mock import MagicMock, patch
# ---------------------------------------------------------------------------
# Import hook module (no .py extension)
@@ -24,8 +24,8 @@ amc_hook.__file__ = str(hook_path)
code = compile(hook_path.read_text(), hook_path, "exec")
exec(code, amc_hook.__dict__) # noqa: S102 - loading local module
# Import spawn mixin
import amc_server.mixins.spawn as spawn_mod
# Import spawn mixin (after hook loading - intentional)
import amc_server.mixins.spawn as spawn_mod # noqa: E402
# ===========================================================================