이슈 #6 구현 #7

Merged
genichin merged 2 commits from code-agent/issue-6-e5da4592756fb704 into main 2026-09-19 11:15:20 +00:00
2 changed files with 44 additions and 7 deletions
Showing only changes of commit d102c02bc1 - Show all commits
+7 -4
View File
@@ -116,17 +116,20 @@ class ChatApp(App[None]):
self.messages.extend((user_message, agent_message))
conversation = self.query_one("#conversation", VerticalScroll)
self._mount_message(conversation, user_message, "user-message")
self._mount_message(conversation, agent_message, "response-message")
self._mount_message(conversation, user_message)
self._mount_message(conversation, agent_message)
conversation.scroll_end(animate=False)
@staticmethod
def _mount_message(
conversation: VerticalScroll,
message: ChatMessage,
style_class: str,
) -> None:
"""메시지 역할을 라벨과 호환 가능한 구조적 스타일로 변환한다."""
"""메시지 역할에서 라벨과 구조적 스타일을 일관되게 파생한다."""
style_class = {
MessageRole.USER: "user-message",
MessageRole.AGENT: "response-message",
}[message.role]
text = f"{message.role.label}: {message.content}"
conversation.mount(
Label(text, classes=f"chat-message {style_class} {message.role.value}-role")
+37 -3
View File
@@ -1,7 +1,7 @@
import unittest
from unittest.mock import patch
from textual.widgets import Input, Label
from textual.widgets import Input
from chat_tui.app import (
RESPONSE_MESSAGES,
@@ -78,13 +78,46 @@ class ChatAppTest(unittest.IsolatedAsyncioTestCase):
labels[1].classes,
frozenset({"chat-message", "response-message", "agent-role"}),
)
self.assertIn("사용자:", str(labels[0].renderable))
self.assertIn("에이전트:", str(labels[1].renderable))
self.assertEqual(
str(labels[0].renderable),
"사용자: 역할 확인",
)
self.assertEqual(
str(labels[1].renderable),
f"에이전트: {RESPONSE_MESSAGES[0]}",
)
self.assertIn("border-left", app.CSS)
self.assertIn("border-right", app.CSS)
self.assertIn("align: left middle", app.CSS)
self.assertIn("align: right middle", app.CSS)
async def test_rendering_styles_are_derived_from_message_roles(self) -> None:
app = ChatApp()
async with app.run_test() as pilot:
conversation = app.query_one("#conversation")
app._mount_message(
conversation,
ChatMessage(MessageRole.USER, "사용자 본문"),
)
app._mount_message(
conversation,
ChatMessage(MessageRole.AGENT, "에이전트 본문"),
)
await pilot.pause()
labels = list(app.query("#conversation Label"))
self.assertEqual(len(labels), 2)
self.assertEqual(
labels[0].classes,
frozenset({"chat-message", "user-message", "user-role"}),
)
self.assertEqual(
labels[1].classes,
frozenset({"chat-message", "response-message", "agent-role"}),
)
self.assertEqual(str(labels[0].renderable), "사용자: 사용자 본문")
self.assertEqual(str(labels[1].renderable), "에이전트: 에이전트 본문")
async def test_response_selection_uses_standard_random_choice(self) -> None:
app = ChatApp()
with patch(
@@ -112,6 +145,7 @@ class ChatAppTest(unittest.IsolatedAsyncioTestCase):
self.assertEqual(app.selected_responses, [])
self.assertEqual(app.messages, [])
self.assertEqual(len(app.query("#conversation Label")), 0)
self.assertEqual(input_widget.value, "")
choice_mock.assert_not_called()
async def test_empty_input_is_not_added(self) -> None: