{"id":19489372,"url":"https://github.com/salute-developers/canvas-example","last_synced_at":"2025-04-25T19:30:28.699Z","repository":{"id":38456588,"uuid":"496900991","full_name":"salute-developers/canvas-example","owner":"salute-developers","description":"Пример простого Canvas App","archived":false,"fork":false,"pushed_at":"2024-04-17T12:29:39.000Z","size":2852,"stargazers_count":3,"open_issues_count":5,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-04T02:11:42.452Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/salute-developers.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-05-27T07:19:31.000Z","updated_at":"2025-03-18T07:53:26.000Z","dependencies_parsed_at":"2023-01-28T13:17:35.099Z","dependency_job_id":"32d7960c-5b44-4fc4-92c2-efe57f88204c","html_url":"https://github.com/salute-developers/canvas-example","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salute-developers%2Fcanvas-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salute-developers%2Fcanvas-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salute-developers%2Fcanvas-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salute-developers%2Fcanvas-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/salute-developers","download_url":"https://codeload.github.com/salute-developers/canvas-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250882379,"owners_count":21502300,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T21:08:19.517Z","updated_at":"2025-04-25T19:30:28.350Z","avatar_url":"https://github.com/salute-developers.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Пример Canvas App (front) + сценарий (back) на next.js\n\nПроект состоит из серверной части (так называемого сценария, доступного по адресу http://localhost:3000/api/hook) и фронтенда (доступного по адресу http://localhost:3000/).\n\n### Первый запуск\n- Установить зависимости (`npm ci`)\n- Заполнить `NEXT_PUBLIC_SMARTAPP_TOKEN` в `.env.development` (можно получить личный токен [тут](https://developers.sber.ru/studio/settings/emulator))\n- Запустить локальный сервер (`npm run dev`)\n- Создать туннель до локального сервера (например установив ngrok и выполнив команду `ngrok http 3000` )\n- Создать новый проект с типом `SmartApp -\u003e Canvas App` в SmartMarket Studio\n- В параметрах проекта указать\n  - Тип проекта -\u003e SmartApp API\n  - Внешняя ссылка -\u003e `\u003chttps ссылка туннеля\u003e/api/hook`\n  - Хостинг фронтенда -\u003e `\u003chttps ссылка туннеля\u003e`\n\n### Отладка в браузере\nК стандартным девайсам доступным для отладки в Google Chrome можно добавить SberBox (см. скриншот) или другое устройство и зайти на http://localhost:3000/ в браузере.\n\n![image](https://user-images.githubusercontent.com/17454987/169296718-602a6ac3-140c-4918-870c-55b5126f02f6.png)\n\n### Отладка в Салют/Sberbox\n\nДля отладки на реальном устройстве нужно войти на устройство под тем же SberId, что и в SmartMarket Studio и сказать голосовому ассистенту активационную фразу `NEXT_PUBLIC_SMARTAPP_INIT_PHRASE` из `.env.development` (например `Запусти туду тест`). Если туннель (см. выше) активен, то этого достаточно, если нет, то вначале надо настроить туннелинг.\n\n### Продакшен сборка\n\n```bash\nnpm run build\nnpm start\n```\n## Внутреннее устройство\n\nШаблон основан на [nextjs](https://nextjs.org/)\n\n### Получение данных\n\nВсе данные для Canvas App поступают из сценария от AssistantClient. То есть любой запрос за данными попадает в AssistantClient и только потом в серверную часть приложения (сценарий). При этом у сценария ровно одна точка входа - `/api/hook`. В примере такие запросы делаются при помощи хуков-оберток над `useQuery/useMutation` из библиотеки [react-query](https://react-query.tanstack.com/overview).\n\nПри этом какие-то запросы могут бы инициированы голосовой командой пользователя, а какие-то взаимодействием пользователя с интерфейсом. Некоторые команды могут быть инициированы обоими способами (например `doneNote`). Во всех случаях полученные от AssistantClient данные записывают в кеш `react-query`.\n\n### Статический рендеринг\n\nВ примере есть поддержка статической генерации страниц. Для разных поверхностей в момент сборки проекта будет сгенерирован статический html для моментальной отдачи пользователю при запросе (без необходимости как клиентского так и серверного рендеринга). При этом `nextjs` поддерживает и [другие способы доставки страниц до пользователя](https://nextjs.org/docs/basic-features/data-fetching/overview).\n\nВ урл будут добавлены суффиксы поверхности и персонажа, например: `/joy/@mobile/`\n\n#### Поверхность\n\nПод поверхностью подразумевается группа устройства, на котором будет работать Canvas App. В данной примере 3 поверхности: мобильное устройство, SberBox и SberPortal.\n\nДля каждой поверхности могут быть разные React компоненты, которые нужны на одной поверхности и не нужны на другой. То есть JS-бандл должен содержать только нужные для текущей поверхности компоненты. Для этого все компоненты (а точнее страницы) поделены на папки (`@mobile`, `@portal`, `@sberbox`), которые соответствуют урлам в `nextjs`. Благодаря этому `nextjs` делает код-сплиттинг по поверхностям (урлам), и компоненты для `@sberbox` не окажутся в JS-бандле для `@mobile`.\n\nКаждая страница в такой директории будет импортировать только необходимые для данной платформы компоненты, избегая импорта, а значит и загрузки, компонентов других поверхностей.\n\nВ пример уникальных для поверхности файлов нет, но в реальных проектах они могут появится.\n\n#### Персонаж\n\nПерсонаж выбирается пользователем и может быть одним из трех заданных (`eva`, `sber`, `joy`). Так как подразумевается, что разделение стилей для персонажей не требуется, используется [динамический роутинг](https://nextjs.org/docs/routing/dynamic-routes), вместо статического.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalute-developers%2Fcanvas-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsalute-developers%2Fcanvas-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalute-developers%2Fcanvas-example/lists"}