패키지 네임스페이스가 아니라 레시피입니다. 어시스턴트 답 안의 도구 호출은 Disclosure이고, 그 summary가 도구 이름과 상태를 나타내는 Badge를 보여 주며, 입력과 출력은 안쪽의 DataList에 담습니다. 승인을 기다리는 호출에는 Button 두 개를 더합니다. 도구가 무엇을 하고 언제 실행되는지는 제품이 정합니다. ToolCall.* export는 없습니다.
이 페이지의 내용
호출을 열어 입력과 출력을 읽어 보세요. 마지막 호출은 승인을 기다립니다. 승인하면 실행되는 모습을 볼 수 있고, 거부할 수도 있습니다.
I'll find where the focus ring color is set, then make a branch for the change.
summary에는 이름을 Code로, 상태를 Badge로 넣으므로 스크린 리더는 둘을 듣고 나서 호출이 열려 있는지 듣습니다. 입력과 출력은 CodeBlock에 담은 JSON이고, 각각 이름 있는 영역이자 탭 정지이므로 키보드로 긴 줄을 스크롤할 수 있습니다. 도구가 많은 것을 돌려줄 수 있으니 출력에는 최대 높이를 주세요.
아래 승인에 나오는 ToolCall로 그린 각 호출은 스트림이 준 순서대로 Message 안의 앞뒤 텍스트 사이에 넣으세요. Message.Root의 파트는 다음 파트와 간격이 생기므로 호출에 따로 margin을 줄 필요가 없습니다. streaming은 호출이 아니라 에이전트의 스트림에서 정하세요. 호출이 실행되는 동안을 포함해 에이전트가 답을 만드는 동안 켜고, 차례가 끝나거나 결정을 기다리며 멈추면 끕니다. 아래 승인의 끝에서 다룹니다.
검색어처럼 입력이 몇 개의 단순한 값인 도구라면, JSON보다 입력의 키를 가로 DataList로 보여 주는 편이 읽기 쉽습니다.
승인
승인을 기다리는 호출은 실행 전에 입력을 읽을 수 있도록 열리고, 그 뒤에 Approve와 Deny를 보여 줍니다. open을 상태에서 정하면 호출은 기다리기 시작할 때 열리고, 실행되거나 거부되면 닫힙니다. React는 그 값이 바뀔 때만 open을 설정하므로, 누군가 직접 연 호출은 끝난 뒤에도 열려 있습니다. 버튼 그룹의 이름은 도구 이름으로 정하세요. 그러면 "Approve"가 무엇을 승인하는지 알 수 있습니다. 기다리는 두 호출이 같은 도구를 쓸 수 있다면, 파일 경로처럼 둘을 구별하는 입력의 값을 그룹의 이름과 각 영역의 이름에 더하세요.
호출이 정해지면 버튼이 사라지고, 그러면 포커스가 페이지로 떨어집니다. 버튼 그룹에 useFocusHandoff를 쓰면, 버튼 중 하나에 포커스가 있는 채로 사라질 때 포커스를 호출의 summary로 옮깁니다. 클릭으로 정했든, 다른 기기처럼 다른 곳에서 결정이 왔든 같습니다. … 자리에는 위 호출의 DataList가 들어갑니다.
기다리는 호출은 에이전트의 차례를 멈추므로, 다른 호출이 아직 실행 중이더라도 어떤 호출이든 기다리는 동안에는 streaming을 끄고, 호출이 승인되든 거부되든 에이전트가 다시 시작할 때 다시 켜세요. 그러면 버튼이 busy 상태인 메시지 안에 놓이지 않습니다. Conversation은 기다리기 시작할 때 메시지가 끝났다고, 차례가 다시 시작되면 메시지가 시작됐다고 알립니다. 이 말은 승인을 언급하지 않습니다. 더 분명한 신호가 필요하면 Conversation.Root에 announceMessages={false}를 주고 시작, 기다림, 끝을 모두 useAnnouncer로 직접 알리세요. Conversation의 "끝났다"는 알림과 함께 기다림을 알리면, 대기 중인 두 메시지 중 새것만 읽히므로 둘 중 하나를 잃습니다.
접근성을 지키는 조합
Todae가 맡는 것:useFocusHandoff는 승인 버튼 중 하나에 포커스가 있는 채로 사라지면 포커스를 summary로 옮깁니다. Disclosure.Root는 네이티브 <details>이므로 열고 닫기, Enter와 Space, 스크린 리더가 듣는 펼침 상태, 닫힌 호출 안의 페이지 내 찾기는 브라우저가 맡습니다. summary의 포커스 링. Badge에는 role이 없으므로 그 라벨은 summary의 텍스트와 함께 읽힙니다.
직접 제공할 것: tone의 색만이 아니라 말로 쓴 상태 라벨. Code 페이지에서처럼 각 CodeBlock의 탭 정지, role, 이름. 승인 버튼 그룹의 이름. 승인 버튼이 사라질 때 포커스가 갈 곳인 summary.
주의할 점: summary 안의 버튼, 링크 같은 컨트롤. summary 자체가 열고 닫는 버튼이라 그 안의 컨트롤은 브라우저마다 같은 방법으로 닿지 않습니다. 끝난 호출을 답에서 없애는 것. 페이지 내 찾기와 스크린 리더가 에이전트가 한 일에 더는 닿지 못합니다. 상태가 바뀔 때마다 알리는 것. 답이 끝나면 Conversation이 이미 알리고, 스스로 실행되고 끝나는 호출은 그 일부입니다.
언제 쓰나요
쓸 때: 채팅에서 어시스턴트가 읽는 사람 대신 실행한 일을 입력, 출력과 함께 보여 줄 때. 검색, 가져오기, 파일 변경 같은 일입니다. 호출이 실행되기 전에 읽는 사람에게 승인을 받을 때.
쓰지 않을 때: 텍스트로 streaming되는 어시스턴트의 추론 → Prose를 담은 Disclosure. 작업의 정해진 단계를 따라가는 진행 → Steps. 계속하기 전에 읽는 사람이 처리해야 하는 결과 → 메시지 안의 Alert.