feat(room): закрепление участника и слежение основного окна за говорящим
Правила выбора фокуса сцены (`pickStageFocus`) дополнены двумя входами: - `pinnedKey` — участник, закреплённый булавкой на плитке. Держит фокус вопреки говорящим, но уступает любой активной демонстрации экрана; как только демонстрация закончилась, фокус возвращается именно на закреплённого (правило стоит выше удержания предыдущего фокуса). Состояние — в `RoomStage`, повторное нажатие снимает, выход закреплённого из комнаты тоже. Закреплённая плитка помечена рамкой и подсвеченной булавкой; на тач-устройствах булавка видна без наведения. - `holdScreenShare` — живая демонстрация в фокусе не уступает заговорившему (основное окно). PiP не затронут: там по-прежнему всегда виден говорящий. Основное окно теперь следует за говорящим (`followSpeaker`) поверх `useSpeakingParticipants()` вместо дребезжащего `participant.isSpeaking`, с удержанием состава в 1.2 с (`useSteadySpeakers`) — короткие реплики фокус не уводят. Среди одновременно говорящих предпочитается тот, у кого включена камера (`cameraKeysWithVideo`). `stageTrackKey` переехал в `stageFocus.ts` — ключ плитки нужен и сцене, и самой плитке (в карусели/гриде она рендерится шаблоном, без пропсов).
This commit is contained in:
@@ -416,6 +416,52 @@ video[data-lk-source='screen_share'] { object-fit: contain; background: #000; }
|
||||
}
|
||||
.stage-sharing-chip button:hover { filter: brightness(0.95); }
|
||||
|
||||
/*
|
||||
* ---------- Кнопка закрепления участника (булавка) ----------
|
||||
* Рендерится внутри `RoomParticipantTile` (см. её `TileBody`), т.е. внутри
|
||||
* `.lk-participant-tile` — тот у @livekit/components-styles уже
|
||||
* `position: relative`. Геометрия и логика появления повторяют штатную
|
||||
* `.lk-focus-toggle-button` той же библиотеки (правый верхний угол, видна по
|
||||
* наведению) — чтобы плитка выглядела цельно, а не «нашей кнопкой поверх
|
||||
* чужой». Отличие: у закреплённой плитки булавка видна ВСЕГДА и подсвечена —
|
||||
* закрепление должно читаться без наведения.
|
||||
*/
|
||||
.room-pin-toggle {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.25rem;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
padding: 0.25rem;
|
||||
border: none;
|
||||
border-radius: calc(var(--lk-border-radius, 0.5rem) / 2);
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: var(--color-room-text-primary);
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
transition-delay: 0.2s;
|
||||
}
|
||||
.room-pin-toggle svg { width: 20px; height: 20px; }
|
||||
.lk-participant-tile:hover .room-pin-toggle,
|
||||
.lk-participant-tile:focus-within .room-pin-toggle,
|
||||
.room-pin-toggle.is-pinned { opacity: 1; transition-delay: 0s; }
|
||||
.room-pin-toggle.is-pinned { background: var(--color-room-mic-on); color: #10331f; }
|
||||
|
||||
/* Тач-устройства (мобильный/планшет): hover нет вовсе — иначе булавка была бы
|
||||
недоступна. Тулбар комнаты не задет: кнопка живёт на плитке. */
|
||||
@media (hover: none) {
|
||||
.room-pin-toggle { opacity: 1; transition-delay: 0s; }
|
||||
}
|
||||
|
||||
/* Метка закреплённой плитки — рамка по контуру. Класс закрепления висит на
|
||||
кнопке (корневой div плитки рендерит библиотека, свой класс туда не
|
||||
добавить), поэтому селектор идёт через `:has()`. */
|
||||
.lk-participant-tile:has(.room-pin-toggle.is-pinned) {
|
||||
outline: 2px solid var(--color-room-mic-on);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* ---------- Заглушка «конференция в мини-окне» (Document PiP) ---------- */
|
||||
.room-pip-placeholder {
|
||||
flex: 1;
|
||||
|
||||
Reference in New Issue
Block a user