본문으로 건너뛰기

회의

회의실 · 음성 채널의 부품입니다. 미디어 SDK 를 모르고, 앱이 넘긴 MediaStream 과 상태(음소거 · 말하는 중 · 연결 품질 · 녹화 중)를 그리기만 합니다.

import
import { … } from "@uyoolabs/ui/meeting";
쓰는 경우
회의실(상단 바 · 무대 · 측면 패널 · 컨트롤 막대), 입장 전 화면, 음성 채널의 참여자 줄과 도크, 자막과 실시간 기록에 씁니다.
쓰지 않는 경우
음소거 · 카메라 꺼짐 · 권한 거부를 빨강으로 그리지 않습니다(오류가 아닙니다). 말하는 중을 초록으로 그리지 않습니다(primary 띠 + 소리 막대). 영상 위 글자를 반투명 판에 얹지 않습니다. 녹화 표시를 빨간 점이나 아이콘만으로 두지 않습니다(결정 #36).
빨강
컨트롤 막대의 나가기 하나, 녹화 중의 정지 모양, 연결 실패 · 오프라인 띠에만 씁니다(결정 #38). 다른 곳의 나가기는 secondary 입니다.

서브패스 ./meeting 은 radix-ui 만 선택 peer 로 가져옵니다. 회의 CSS 는 @uyoolabs/ui/css 에 들어 있습니다.

MeetingRoom · MeetingPanel

회의실 틀입니다. 상단 바 · 무대 · 측면 패널 · 컨트롤 막대 네 띠로 나누고 F6 으로 옮깁니다. 이름은 브레드크럼이 말하고 h1 을 두지 않습니다.

조립 견본은 P-Meeting 보드와 픽스처 meeting-room(다크 고정 · 화면공유 · 자막 · 녹화 · 기록 패널)에 있습니다. 다크 고정은 틀 안의 토큰만 바꿉니다 — body 에 붙는 메뉴 · 툴팁 · 대화상자는 앱 테마를 따릅니다.

키동작
F6상단 바 → 무대 → 컨트롤 → 패널. Shift+F6 은 거꾸로

VideoTile

사람(또는 공유 화면) 하나의 창입니다. 판지에 낸 창이라 두께가 없고, 이름 판은 불투명합니다. 말하는 중은 안쪽 primary 띠 + 소리 막대, 음소거는 빗금 마이크로 말합니다.

접근성 이름은 이름 + 음소거 + 손 듦만 담습니다(‘김하늘, 음소거’). 말하는 중은 바뀔 때마다 이름을 다시 읽는 소음이라 넣지 않습니다(결정 #42). 손 듦 · 고정은 role=status 로 한 번 읽습니다. stream 이 바뀌면 같은 video 요소의 srcObject 만 바꿉니다.

MeetingStage

참가자와 공유 화면을 받아 격자 · 발표자 · 공유 배치를 고르고 타일을 늘어놓습니다. 격자는 자리를 바꾸지 않고 테두리만 옮깁니다.

키동작
Tab무대의 활성 타일 하나로 들어온다(roving tabindex)
←·→·↑·↓타일 사이 이동(격자는 상하좌우, 발표자 · 공유는 앞뒤)
Home·End첫 · 마지막 타일
Page Up·Page Down쪽 넘김(많을 때). 바뀐 쪽은 ‘참가자 9–16 / 20’ 으로 읽는다
Enter·Shift+F10타일 메뉴(고정 · 전체 화면 · 호스트 동작)
⌘⇧P·Ctrl+Shift+P지금 타일 고정 · 해제
Esc메뉴를 닫고 타일로 돌아온다

CallControls

회의실 바닥의 큰 키캡 막대입니다. 켠 토글은 눌린 채로 가라앉고 아이콘에 빗금이 생깁니다. 토글은 이름이 고정이고 aria-pressed 가 상태를 말합니다(결정 #37).

키동작
⌘D·Ctrl+D음소거 · 해제(입력칸 안에서도)
⌘E·Ctrl+E카메라 끄기 · 켜기
Space누르는 동안 말하기(음소거일 때, 입력칸 밖 · IME 조합 중 제외). 떼거나 창이 흐려지면 다시 음소거
⌘⇧H·Ctrl+Shift+H손 들기 · 내리기
⌘⇧C·Ctrl+Shift+C자막 켜기 · 끄기
←·→·Home·End막대 안 이동(APG Toolbar). 꺾쇠 메뉴는 ↑ 로도 연다

⌘D · ⌘E 는 브라우저 북마크 · 검색과 겹칩니다. 막대가 그려져 있는 동안에만 붙고, 처리한 키만 막습니다. 호스트만 할 수 있는 키는 숨기지 않고 비활성 + 이유 툴팁으로 둡니다.

PreJoin

회의에 들어가기 직전 내 모습 · 소리를 확인하고 장치와 이름을 정합니다. 권한이 없거나 장치가 없어도 ‘소리 없이 참여’로 들어갈 길을 남깁니다. 녹화 중인 회의는 여기서 먼저 알립니다.

카메라를 켜는 중입니다

잘 들립니다

내 화면은 거울처럼 보입니다. 다른 사람에게는 뒤집지 않은 화면이 갑니다.

주간 점검

#결제-개편 · 14:00–14:45김하늘 외 3명이 참여 중입니다

이 회의는 녹화 중입니다. 참여하면 내 화면과 음성도 녹화에 담깁니다.
키동작
Tab마이크 키 → 카메라 키 → 설정 → 이름 → 장치 셋 → 참여 → 소리 없이 참여
⌘D·Ctrl+D음소거 켜기 · 끄기(회의실과 같다)
⌘E·Ctrl+E카메라 끄기 · 켜기
Enter이름 칸에서 참여(IME 조합 중 Enter 는 무시)
Esc대기실에서 기다리기 취소

RecordingIndicator · 녹화 대화상자

녹화 · 기록이 켜진 동안 모든 참가자의 모든 화면에 늘 보이는 반전 칸입니다. 빨간 점 대신 녹화 원 + 글자로 말합니다. 시작은 호스트의 동의 확인을 거치고, 시작되는 순간 모든 참가자가 계속 참여할지 고릅니다.

키동작
Enter·Space녹화 표시: 정보 팝오버 열기 · 닫기
Esc팝오버 · 시작 · 중지 대화상자를 닫는다. 참가자 고지는 닫히지 않는다(둘 중 하나를 고른다)
Tab대화상자 안에서 돈다. 처음 포커스: 시작은 동의 체크, 고지는 ‘계속 참여’, 중지는 ‘계속 녹화’

대화상자의 기본 문구는 예시입니다. 법무 검토를 거친 고지 문구 · 보관 기간 설명을 labels 로 덮어 씁니다(결정 #40). 컴포넌트는 누가 · 언제(UTC → 보는 사람 시간대) · 무엇이 · 어디에 · 얼마나의 자리를 강제합니다.

ConnectionBanner

연결 문제가 이어지는 동안 무대 맨 위에 남는 띠입니다(토스트가 아닙니다). 저절로 고쳐지는 문제는 주의, 사용자가 움직여야 하는 문제만 오류, 다시 연결됨은 성공(4초)입니다.

연결이 끊겨 다시 연결하는 중입니다. 2번째 시도 · 소리와 영상이 잠시 멈춥니다.
키동작
Tab띠의 동작 → 닫기(무대 앞). 띠는 포커스를 빼앗지 않는다
Esc띠 안에 포커스가 있으면 닫을 수 있는 띠(품질 · 다시 연결됨 · 마이크)를 닫는다

VoiceChannelMembers · VoiceDock

음성 채널은 누르면 바로 들어가는 방입니다. 채널 아래에 참여자 줄이 보이고, 들어가면 사이드바 바닥에 연결 도크가 남습니다.

  • 김하늘
  • 박도윤, 음소거
  • 최유나, 화면 공유 중
  • 이서연(나)

음성 연결됨

디자인 리뷰 · 한빛랩

Captions · Transcript

자막은 무대 아래 가운데의 불투명 판(두 줄, 보는 사람 하나의 설정)이고, 기록은 측면 패널의 흐르는 기록입니다. 기록을 저장하는 동안은 모두에게 ‘기록 중’을 보입니다.

기록

기록 중
  1. 김하늘

    결제 실패 알림은 이번 배포에 넣기로 했습니다.

  2. 박도윤

    그럼 환불 정책 문구는 다음 주 화요일까지 정리해서 법무팀에 보내 드릴게요.

키동작
Enter·Shift+Enter찾기 칸에서 다음 · 이전 결과(IME 조합 중 Enter 는 무시)
Esc찾기 글을 지우고, 다시 누르면 패널을 닫는다
End기록 안에서 최신으로

자막은 role=log · aria-live=off 입니다(스크린 리더는 기록 패널에서 읽습니다). 기록은 role=log · polite 이고 확정된 문장만 읽습니다(결정 #42). 확정 전 글은 보조 색이고 aria-hidden 입니다.

props

소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.

MeetingRoom

prop타입기본값설명
tone"auto" | "dark""auto"회의실 테마. auto 는 앱 테마를 따르고, dark 는 회의실만 다크로 고정한다(결정 #9). 고정은 이 틀 안의 토큰만 바꾼다 — body 에 붙는 메뉴 · 툴팁 · 대화상자는 앱 테마를 따른다
topReactNode—상단 바: 브레드크럼(회의 이름) · 녹화 표시 · 시계 · 참가자 묶음. 제품 화면이라 h1 을 두지 않는다
bannerReactNode—무대 맨 위 연결 띠(ConnectionBanner). Tab 순서상 무대 앞
panelReactNode—측면 패널(MeetingPanel). 없으면 무대가 넓어진다
controlsReactNode—바닥 컨트롤 막대(CallControls)
children필수ReactNode—무대(MeetingStage)
lang"ko" | "en" | "ja"—
labelsPartial<MeetingRoomLabels>—
classNamestring—

MeetingPanel

prop타입기본값설명
title필수ReactNode—머리 제목(h2)
actionsReactNode—제목 옆(‘기록 중’ 표시 · 탭)
onClose() => void—있으면 닫기 단추
toolsReactNode—머리 아래 도구 줄(찾기 칸)
footerReactNode—바닥(내려받기 · AI 요약)
children필수ReactNode—본문(스크롤 자리)
bodyRefRef<HTMLDivElement>—본문 스크롤 요소. 흐르는 기록의 따라가기에 쓴다
onBodyScroll() => void—본문 스크롤(바닥 따라가기가 사람이 위로 올린 것을 안다)
idstring—패널 키의 aria-controls 가 가리킬 id
lang"ko" | "en" | "ja"—
labelsPartial<MeetingRoomLabels>—
classNamestring—

VideoTile

prop타입기본값설명
participant필수MeetingParticipant—
streamMediaStream | null—앱이 받은 MediaStream. 바뀌면 video.srcObject 만 바꾼다(요소를 다시 만들지 않는다 — 깜빡임)
mediaReactNode—앱이 직접 그린 미디어(자기 <video> · 포스터 그림). 주면 stream 대신 이것을 창 안에 채운다. 실기기가 없는 문서 · 픽스처 · SDK 가 요소를 직접 주는 경우에 쓴다
kind"camera" | "screen"—screen 이면 자르지 않고 담고(contain) 이름이 ‘○○의 화면’이다
camera"on" | "off" | "connecting" | "paused"—기본: stream · media 가 있으면 on, 없으면 off
speakingboolean—
mutedboolean—
quality"good" | "fair" | "poor" | "lost"—good 은 그리지 않는다(나빠질 때만 보인다)
handRaisednumber | null—손 든 순번(1부터). 없으면 안 든 것
reaction{ emoji: string; label: string } | null—지금 떠 있는 반응. 4초 뒤 앱이 지운다
selfboolean—나. 이름 뒤에 ‘(나)’
mirrorboolean—거울. 기본 self && kind === "camera"
pinnedboolean—제어 · 비제어 둘 다
defaultPinnedboolean—
onPinnedChange(pinned: boolean) => void—
menuReactNode—타일 메뉴에 더할 항목(호스트 동작은 앱이 넘긴다). 없으면 고정 · 전체 화면만
showMenuboolean—false 면 메뉴 단추를 그리지 않는다(입장 전 미리보기)
onVisibleChange(visible: boolean) => void—보이는지. IntersectionObserver 로 재서 알린다(보이지 않는 타일은 영상을 받지 않게 — 대역폭)
onPlaybackBlocked() => void—자동 재생이 막혔을 때(Safari 의 play() 거부). 타일은 ‘누르면 영상이 보입니다’ 단추를 그린다
size"lg" | "md" | "sm" | "xs"—lg 무대 · md 격자 · sm 필름스트립(168) · xs 아주 작음(120, 이름 판만)
noticeReactNode—카메라가 꺼진 동안 가운데 아바타 대신 그릴 안내(입장 전 권한 · 장치 상태). role=status 로 바뀔 때 읽는다
aria-labelstring—접근성 이름을 바꿀 때(입장 전 ‘내 미리보기’)
childrenReactNode—위에 겹칠 것(입장 전 미리보기의 장치 키)
lang"ko" | "en" | "ja"—
labelsPartial<VideoTileLabels>—
classNamestring—
styleCSSProperties—
refRef<HTMLDivElement>—

MeetingStage

prop타입기본값설명
tiles필수StageTile[]—참가자 타일. 들어온 순서대로 넘긴다(격자는 이 자리를 지킨다)
screenShareStageTile | null—공유 화면 타일(kind="screen"). 있으면 auto 가 share 가 된다
layout"auto" | "grid" | "speaker" | "share"—제어 · 비제어. 기본 "auto"(공유 있으면 share, 고정이 있으면 speaker, 없으면 grid)
defaultLayout"auto" | "grid" | "speaker" | "share""auto"
onLayoutChange(layout: StageLayout) => void—
pinnedIdstring | null—제어 · 비제어. 고정한 사람(나에게만 적용)
defaultPinnedIdstring | nullnull
onPinnedChange(id: string | null) => void—
activeSpeakerIdstring | null—지금 말하는 사람. 발표자 배치의 큰 자리 · 필름스트립 순서에 쓴다
speakingLogSpeakingEntry[][]최근 말한 기록. 필름스트립 순서(300ms 이상 말한 사람을 보이는 자리로)
maxPerPagenumber9한 쪽 최대 칸. 기본 9. 넘치면 마지막 칸이 ‘+N명’이다
pagenumber—제어 · 비제어. 0 부터
defaultPagenumber0
onPageChange(page: number) => void—
onVisibleChange(ids: string[]) => void—화면에 그려진(영상을 받아야 하는) 참가자 id. 바뀔 때만 부른다
onShowAll() => void—‘+N명’ 칸을 누르면(참가자 패널 열기 — 포커스는 패널의 찾기 칸으로)
hideSelfbooleanfalse내 화면 숨기기(혼자일 때는 숨기지 않는다)
childrenReactNode—자막 판처럼 무대 위에 겹칠 것
lang"ko" | "en" | "ja"—
labelsPartial<MeetingStageLabels>—
classNamestring—

CallControls

prop타입기본값설명
micMuted필수boolean—
onMicMutedChange필수(muted: boolean) => void—
cameraOff필수boolean—
onCameraOffChange필수(off: boolean) => void—
devicesDevice[]—있으면 마이크 · 카메라 키에 장치 꺾쇠 메뉴를 붙인다
selectedDeviceIdsDeviceSelection{}
onDeviceChange(kind: DeviceKind, id: string) => void—
sharingbooleanfalse내가 화면을 공유하는 중
onSharingChange(sharing: boolean) => void—없으면 공유 키를 그리지 않는다
canShareboolean | { reason: string }truefalse 면 비활성, { reason } 이면 비활성 + 이유 툴팁
recording"idle" | "starting" | "on" | "paused""idle"녹화 상태. idle 이 아니면 녹화 키가 ‘녹화 중지’(정지 모양 빨강)가 된다
onRecordRequest(action: "start" | "stop") => void—녹화 키를 누르면. 시작이면 RecordingStartDialog, 중지면 RecordingStopDialog 를 앱이 연다. 없으면 녹화 키가 없다
canRecordboolean | { reason: string }true
handRaisedbooleanfalse
onHandRaisedChange(raised: boolean) => void—
reactionsArray<{ emoji: string; label: string }>—반응 목록. 기본 여섯(👍 👏 😂 ❤️ 🎉 😮)
onReact(emoji: string) => void—없으면 반응 키를 그리지 않는다
captionsOnbooleanfalse
onCaptionsChange(on: boolean) => void—없으면 자막 키를 그리지 않는다
panelPanelId | null—제어 · 비제어. 열린 측면 패널
defaultPanelPanelId | nullnull
onPanelChange(panel: PanelId | null) => void—
panelIdstring—패널 키(aria-controls)가 가리킬 측면 패널 id
panelsPanelId[]["people", "chat", "ai"]그릴 패널 키. 기본 셋 다
counts{ people?: number; unreadChat?: number }{}
onLeave필수() => void—나가기. 확인 없이 바로(한 번에 나가야 한다)
isHostbooleanfalse
onEndForAll() => void—호스트의 나가기 꺾쇠 → ‘모두에게 회의 끝내기’. 앱이 AlertDialog 로 한 번 묻는다
onTransferHostAndLeave() => void—
layout"auto" | "grid" | "speaker" | "share"—더 보기 메뉴의 배치 라디오
onLayoutChange(layout: StageLayout) => void—
moreItemsReactNode—더 보기 메뉴에 더할 항목(DropdownMenuItem)
speakingWhileMutedbooleanfalse앱이 ‘음소거 중 말함’을 2초 이상 감지하면 true. 한 회의에 3번까지 토스트로 알린다(ToastRegion 필요)
pushToTalkbooleantrue누르는 동안 말하기(Space). 기본 true
density"auto" | "comfortable" | "compact""auto"기본 "auto"(640 미만 compact)
shortcutTargetHTMLElement | Document | null—전역 단축키를 붙일 대상. 기본 document. 막대가 그려져 있는 동안만 붙는다
titleReactNode—왼쪽 회의 이름 · 경과 시간
lang"ko" | "en" | "ja"—
labelsPartial<CallControlsLabels>—
classNamestring—

CallKey

prop타입기본값설명
icon필수IconName | ReactElement—아이콘 이름(./icons) 또는 아이콘 요소. 켠 토글은 빗금 아이콘으로 바꿔 넘긴다
label필수string—화면 이름 = 접근성 이름. 상태가 바뀌어도 고정이다(결정 #37)
ariaLabelstring—접근성 이름이 화면 이름과 다를 때(개수를 더한 ‘참가자 8명’). 화면 이름으로 시작해야 음성 입력으로 부를 수 있다 (WCAG 2.5.3)
pressedboolean—토글이면 준다 → aria-pressed. 눌린 채로 가라앉는다
expandedboolean—패널 · 메뉴를 여는 키 → aria-expanded
controlsstring—여는 패널의 id(aria-controls)
variant"default" | "danger""default"danger 는 되돌릴 수 없는 나가기 하나에만(결정 #38)
state"recording"—recording: 녹화 중의 녹화 키. 정지 모양만 빨강
countnumber—키 오른쪽 위 개수 칩. 숫자는 장식이라 label 에 개수를 넣는다
countTone"neutral" | "strong""neutral"strong 은 새 것(primary 면)
shortcutstring—"Mod+D" · "Mod+Shift+H". aria-keyshortcuts 와 툴팁 키캡이 된다(Mod = Mac ⌘ · 그 밖 Ctrl)
tooltipstring—툴팁 글. 토글은 누르면 일어날 다음 동작(‘음소거 해제’). 없으면 label
disabledboolean | { reason: string }—true 면 비활성, { reason } 이면 비활성 + 이유 툴팁(호스트만 할 수 있는 키는 숨기지 않는다)
busyboolean—처리 중(녹화를 시작하는 중)
onClick() => void—
caret{ label: string; menu: ReactNode }—붙은 꺾쇠 키(장치 고르기 · 나가기 방법). 막대가 바닥이라 메뉴는 위로 열고 ↑ 로도 연다
refRef<HTMLButtonElement>—
classNamestring—

PreJoin

prop타입기본값설명
meeting필수MeetingInfo—
streamMediaStream | null—미리보기 스트림. 없으면 카메라 꺼짐 · 권한 상태 화면
mediaReactNode—실기기 없이 그릴 미리보기(문서 · 픽스처의 포스터)
permission필수"prompt" | "granted" | "denied" | "unavailable" | "in-use"—
onRetryPermission() => void—
devices필수Device[]—
selectedDeviceIds필수DeviceSelection—장치 선택은 제어만(앱이 기억 · 저장한다)
onDeviceChange필수(kind: DeviceKind, id: string) => void—
onTestSpeaker() => void—
micMutedboolean—제어 · 비제어 둘 다. 기본 false(켜진 채 시작)
defaultMicMutedbooleanfalse
onMicMutedChange(muted: boolean) => void—
cameraOffboolean—
defaultCameraOffbooleanfalse
onCameraOffChange(off: boolean) => void—
micLevelnumber00–1. 앱이 AnalyserNode 로 재서 넘긴다(rAF 당 1회 이하)
displayNamestring—
defaultDisplayNamestring""
onDisplayNameChange(name: string) => void—
nameRequiredbooleanfalse손님이면 이름 칸이 필수
identitynumber1미리보기 아바타 색(1–12)
onJoin필수(choice: PreJoinChoice) => void—
joiningbooleanfalse
waiting"lobby" | "declined" | "full" | nullnull대기실 · 거절 · 인원 초과
onCancelWaiting() => void—
onRequestAgain() => void—
autoFocusbooleantrue처음 포커스를 참여 버튼(이름이 필수이고 비었으면 이름 칸)에 둔다. 기본 true
timeZonestring—시각을 그릴 시간대. 기본 서울
lang"ko" | "en" | "ja"—
labelsPartial<PreJoinLabels>—
classNamestring—

MicLevel

prop타입기본값설명
level필수number—0–1. 앱이 AnalyserNode 로 재서 넘긴다(rAF 당 1회 이하)
mutedbooleanfalse
lang"ko" | "en" | "ja"—
labelsPartial<Pick<PreJoinLabels, "levelOk" | "levelSilent" | "levelLoud" | "mutedNote">>—
classNamestring—

RecordingIndicator

prop타입기본값설명
state필수"starting" | "on" | "paused" | "transcribing"—starting 솟은 면 + 스피너 · on 반전 면 · paused 솟은 면 + 경계 · transcribing 반전 면(‘기록 중’)
notice필수RecordingNotice—
sincestring—경과 시간의 기준(UTC). 없으면 notice.startedAt
elapsedSecondsnumber—앱이 센 경과 초. 주면 컴포넌트가 시계를 돌리지 않는다(일시 정지 시간을 빼는 앱 · 고정 스냅샷)
compactbooleanfalse아주 좁을 때: 글자 ‘녹화’ + 원만, 시간은 옆 칸. 글자는 남긴다(아이콘만으로는 고지가 아니다)
showTimebooleantrue경과 시간을 그린다. 기본 true(음성 도크처럼 자리가 좁은 곳은 false)
canControlbooleanfalse호스트: 정보 팝오버에 일시 정지 · 중지
onPause() => void—
onResume() => void—
onStopRequest() => void—RecordingStopDialog 를 연다
announcebooleantrue시작 · 일시 정지 · 다시 시작을 role=status 로 한 번 읽는다. 참가자 고지 대화상자가 같은 일을 하는 화면에서는 false(중복으로 읽지 않는다). 기본 true
timeZonestring—시작 시각을 그릴 시간대. 기본 서울
lang"ko" | "en" | "ja"—
labelsPartial<RecordingLabels>—기본 문구는 예시다. 고지 문구는 제품 법무가 정한 것으로 덮는다(결정 #40)
classNamestring—

RecordingStartDialog

prop타입기본값설명
open필수boolean—
onOpenChange필수(open: boolean) => void—
notice필수Omit<RecordingNotice, "startedBy" | "startedAt">—고지 내용. retentionDays 는 필수다 — 없으면 타입 오류로 대화상자를 그릴 수 없다
onConfirm필수() => void—동의 체크 전에는 부를 수 없다
confirmingbooleanfalse
lang"ko" | "en" | "ja"—
labelsPartial<RecordingLabels>—기본 문구는 예시다(결정 #40)

RecordingNoticeDialog

prop타입기본값설명
open필수boolean—
notice필수RecordingNotice—
onStay필수() => void—
onLeave필수() => void—나가기는 안전한 선택이라 secondary(빨강 아님)
timeZonestring—
lang"ko" | "en" | "ja"—
labelsPartial<RecordingLabels>—기본 문구는 예시다(결정 #40)

RecordingStopDialog

prop타입기본값설명
open필수boolean—
onOpenChange필수(open: boolean) => void—
durationSeconds필수number—지금까지 녹화한 길이(초). 분으로 반올림해 말한다
destination필수RecordingNotice["destination"]—
onStop필수() => void—되돌릴 수 없는 동작이라 danger
stoppingbooleanfalse
lang"ko" | "en" | "ja"—
labelsPartial<RecordingLabels>—

ConnectionBanner

prop타입기본값설명
issues필수ConnectionIssue[]—동시에 여럿이면 급한 하나만 그린다(실패 > 오프라인 > 재연결 > 품질 > 장치 > 다시 연결됨)
onRetry() => void—재연결 중 ‘지금 다시 시도’
onRejoin() => void—실패 ‘다시 들어가기’
onTurnOffVideo() => void—품질 ‘내 영상 끄기’
onOpenDevices() => void—장치 ‘장치 설정’
onDismiss(kind: ConnectionIssue["kind"]) => void—닫을 수 있는 것(degraded · restored · no-mic)을 닫았을 때. restored 는 시간이 지나 거둘 때도 부른다
restoredMsnumber4000restored 를 거두는 시간(ms). 기본 4000
density"stage" | "dock""stage"stage 는 무대 맨 위 띠, dock 은 음성 도크 안 한 줄(동작 하나)
lang"ko" | "en" | "ja"—
labelsPartial<ConnectionBannerLabels>—
classNamestring—

VoiceChannelMembers

prop타입기본값설명
channelId필수string—
channelName필수string—목록 이름(‘디자인 리뷰 참여자’)에 쓴다
members필수VoiceMember[]—
selfIdstring—
memberMenu(member: VoiceMember) => ReactNode—사람 줄 메뉴 항목(프로필 · 음량 · 호스트 동작). 주면 줄에 ⋯ 단추가 생긴다
lang"ko" | "en" | "ja"—
labelsPartial<VoiceLabels>—
classNamestring—

VoiceDock

prop타입기본값설명
status필수"connecting" | "connected" | "degraded"—
channel필수{ name: string; workspace?: string; href?: string }—
recordingRecordingNotice | null—있으면 ‘녹화 중’ 칸(모든 참가자에게 늘 보인다)
micMuted필수boolean—
onMicMutedChange필수(muted: boolean) => void—
deafened필수boolean—소리 끄기 = 듣기도 끔. 음소거를 포함한다
onDeafenedChange필수(deafened: boolean) => void—
sharingbooleanfalse
onSharingChange(sharing: boolean) => void—없으면 공유 키를 그리지 않는다
onLeave필수() => void—
bannerReactNode—연결 띠(ConnectionBanner density="dock")를 끼울 자리
lang"ko" | "en" | "ja"—
labelsPartial<VoiceLabels>—
classNamestring—

Captions

prop타입기본값설명
lines필수CaptionLine[]—인식 결과. 마지막 두 사람 몫만 그린다
state"starting" | "on" | "error""on"
onRetry() => void—
size"sm" | "md" | "lg""md"14 · 16 · 20. 기본 md. 보는 사람 하나의 설정이다
spokenLang"auto" | "ko" | "en" | "ja""auto"말하는 언어(인식). 보이는 글의 lang 속성이 된다 — 일본어는 글자 사이에서 줄을 바꾼다
floatingbooleanfalse무대 아래 가운데에 띄운다(MeetingStage 의 children 으로 둘 때)
lang"ko" | "en" | "ja"—
labelsPartial<CaptionLabels>—
classNamestring—

Transcript

prop타입기본값설명
items필수TranscriptItem[]—
targetIdstring | null—인용 · 링크로 건너온 항목. 그 자리로 굴리고 옅은 면으로 짚는다
querystring—제어 · 비제어. 찾기 글
defaultQuerystring""
onQueryChange(query: string) => void—
recordingbooleanfalse기록을 저장하는 중이면 머리에 ‘기록 중’(모두에게 보이는 표시와 같은 규칙)
onDownload() => void—
onSummarize() => void—AI 요약 카드를 만든다(./ai)
onClose() => void—패널 닫기. 찾기 칸이 비었을 때 Esc 도 같다
idstring—패널 키의 aria-controls 가 가리킬 id
timeZonestring—시각을 그릴 시간대. 기본 서울
lang"ko" | "en" | "ja"—
labelsPartial<TranscriptLabels>—
classNamestring—