Общие права за столом
Обычное встраивание листа не требует от вас системы прав — и ничего общего между листами не создаёт: каждый игрок открыл свой лист, события пришли к вам, а видят ли игроки листы друг друга — вопрос за пределами интеграции.
Группа закрывает именно его. Это одна общая сущность на стол: игроки подключают в неё свои листы и получают доступ к листам друг друга, а мастер читает все подключённые. Ваш стол при этом по-прежнему не хранит прав и не проверяет доступ — он только сводит людей в группу.
Что именно даёт группа
Заголовок раздела «Что именно даёт группа»- Игрок подключил свой лист — остальные участники группы получают доступ к нему на чтение. Мастер группы читает любой подключённый лист.
- Права не хранятся списком: доступ вычисляется из членства. Человек вышел из группы — доступ пропал сам, вычищать нечего.
- Членство создаёт сам игрок, своим кликом, в своей сессии. Молча подключить чужой лист нельзя — ни вам, ни мастеру.
- Группа без активности со временем уходит в мягкий архив и возвращается сама, как только владелец переподключается по сохранённому коду.
Регистрация приложения
Заголовок раздела «Регистрация приложения»Разовый шаг, после которого всё остальное — код. Для регистрации требуется два момента:
- Домен вашего приложения — тот, с которого будут открываться страницы с iframe. Именно
рабочий домен:
localhostв список не добавляется. - Желаемый слаг — короткое имя вашего проекта латиницей в kebab-case (
a-z, цифры, дефис), напримерroll-30.
Что вы получите:
- Ваш домен попадёт в CSP
frame-ancestors— без этого браузер просто не покажет наши страницы у вас на сайте. - Слаг попадёт в реестр клиентов. Вы будете использовать его для работы с группами. По нему, например, мастер сможет увидеть все свои группы, созданные в контексте вашего приложения.
Куда писать: @shakusky в Telegram, shakusky.lss в Discord или в
сообщения сообщества VK.
Все страницы групп открываются с параметром ?client=<ваш слаг>. Без него страница честно
скажет, что клиент не представился, и ничего не создаст.
Четыре события
Заголовок раздела «Четыре события»| Событие | Направление | Payload |
|---|---|---|
lss:group-selected |
страница группы → хост | { groupId, code, name } |
lss:group-code |
хост → лист игрока | { code: string | null } |
lss:group-leave |
хост → лист игрока | { code: string } |
lss:group-status |
лист игрока → хост | { connected: boolean } |
Все четыре помечены как экспериментальные: они типизированы и проведены сквозь лист, но их API на
стороне хоста может измениться до стабилизации. Подписка — через source.onEvent.
Шаг 1. Первая настройка
Заголовок раздела «Шаг 1. Первая настройка»Встройте страницу управления группой куда-нибудь в свой интерфейс мастера. Страница живёт у нас, требует того же sandbox, что и лист, и сама попросит войти в аккаунт Long Story Short:
import { createBridgeSheetSource, SHEET_IFRAME_SANDBOX } from '@longstoryshort/vtt-sdk';
const iframe = document.createElement('iframe');iframe.src = 'https://longstoryshort.app/iframe/group/?client=my-table';iframe.setAttribute('sandbox', SHEET_IFRAME_SANDBOX);gmPanel.appendChild(iframe);
const groupPage = createBridgeSheetSource({ iframe, allowedOrigins: ['https://longstoryshort.app'],});
groupPage.onEvent((event) => { if (event.type === 'lss:group-selected') { const { groupId, code, name } = event.payload; myVTT.room.saveLssGroup({ groupId, code, name }); // ← ваше хранилище }});Событие приходит один раз на группу — повторно на перерисовках оно не шлётся, так что обработчик может писать в своё хранилище без защиты от дублей.
Шаг 2. Следующие сессии
Заголовок раздела «Шаг 2. Следующие сессии»На всех последующих загрузках открывайте ту же страницу с сохранённым кодом:
https://longstoryshort.app/iframe/group/?client=my-table&code=<сохранённый код>Это проверка статуса, а не выбор группы: страница ответит только тому аккаунту, который эту
группу создал, и снова пришлёт lss:group-selected. Заодно она снимает архив — вернувшийся
владелец однозначно означает «группа живая», отдельной кнопки для этого не нужно.
Если код мёртв (группу удалили) или мастер вошёл не под тем аккаунтом, страница скажет об этом и предложит создать новую группу. Особой обработки на вашей стороне не требуется.
Шаг 3. Подключение игрока
Заголовок раздела «Шаг 3. Подключение игрока»Код группы доставляется в лист конкретного игрока — в тот самый iframe, который вы уже встроили для него по обычному сценарию:
const sheet = createBridgeSheetSource({ iframe: playerSheetIframe, allowedOrigins: [...] });
const delivered = sheet.send({ type: 'lss:group-code', payload: { code: myVTT.room.lssGroup.code },});
if (!delivered) { myVTT.notification.show('Откройте панель с листом, чтобы присоединиться к группе', 'warning');}Дальше лист сам покажет игроку приглашение — с названием группы и предупреждением, что её участники получат доступ к листу. Присоединение остаётся его собственным действием: стол не создаёт членство за игрока. Если персонаж уже в этой группе, приглашение не появится вовсе.
{ code: null } снимает ранее сделанное предложение — например, если стол потерял свою группу.
Это не отключение: персонаж, который уже вступил, остаётся в группе. Убрать его — следующий
шаг.
Шаг 4. Убрать игрока
Заголовок раздела «Шаг 4. Убрать игрока»Игрок ушёл из-за стола или мастер его выгнал — стол отправляет в тот же лист:
sheet.send({ type: 'lss:group-leave', payload: { code: myVTT.room.lssGroup.code } });Персонаж выходит из группы, и доступ к листу у остальных участников пропадает сам. Подтверждения
у игрока не спрашивают: в отличие от вступления, здесь доступ только отбирается. В ответ придёт
lss:group-status с connected: false.
Две границы, о которых стоит знать:
- Код обязателен, и он должен быть тем самым. Лист выполняет отключение только для группы, которую предложил этот же стол. Так стол может отменить собственное приглашение — и ничего больше: до чужих групп, в которых состоит персонаж, он не дотянется.
- Работает, пока лист открыт у вас. Отключение выполняет сессия самого игрока, поэтому убрать того, кто закрыл лист, стол не может — это остаётся задачей владельца группы.
Шаг 5. Кто подключён
Заголовок раздела «Шаг 5. Кто подключён»Если нужен список «подключён / не подключён», вы рисуете его сами, по одному событию на игрока:
sheet.onEvent((event) => { if (event.type === 'lss:group-status') { myVTT.roster.setConnected(playerId, event.payload.connected); }});lss:group-status — событие состояния: подписавшись позже, вы сразу получите последнее
известное значение, а не будете ждать следующего изменения.
Список групп мастера
Заголовок раздела «Список групп мастера»Отдельная страница на случай, если мастер потерял сохранённый код или вы хотите дать явный выбор:
https://longstoryshort.app/iframe/groups/?client=my-tableОна показывает только группы этого приложения и только те, которыми мастер владеет; выбор шлёт то
же lss:group-selected.
Что стоит знать
Заголовок раздела «Что стоит знать»- Код — пароль на вход, а не ключ управления. Зная код, посторонний может попробовать войти в
группу, но не управлять ею:
?code=на странице группы отвечает только её владельцу. - Группы разных приложений не смешиваются: список фильтруется по вашему слагу.
- Ваш стол не хранит прав. Всё, что у вас появляется, —
groupId,codeи то, что вы сами решили нарисовать поlss:group-status.
