Доброго времени суток, уважаемые пользователи Хабра.
Я не то что бы профессиональный разработчик на C++, в основном я занимаюсь геймдевом на UE5 (по крайней мере последнее время). Но последнее время достаточно часто я балуюсь разработкой десктоп приложений под Windows. Для красоты и простоты я задумывался об использовании именно react. Но из выбора что я увидел было 2 варианта:
Tauri - Фреймворк под Rust с отрисовкой фронтенда сделанном на React и т.п.
Electron - фреймворк для разработки кроссплатформенных настольных приложений с использованием веб-технологий (скопировал описание с гугла)
Electron я отмел сразу, так как хотел писать бэкенд на чем то более удобном чем JS/TS для себя. Tauri мне очень понравился, но изучение Rust заняло немного времени. И смотря на это все я подумал, что можно реализовать какой-нибудь аналог Tauri используя C++.
Немного посидев поизучав информацию, я примерно выстроил себе план работы, из этого вылился первый MVP Shine. Фреймворк на данный момент поддерживает только Windows и тесно связан с vcpkg, так как находится на очень ранней стадии разработки, но его уже вполне себе можно потыкать и использовать.
А теперь к установке и использовании:
Для начала то, что нам нужно для запуска:
CMake
Node.JS
CLion (можете использовать что-то своё, мне комфортнее в нём)
Дальше мы открываем терминал и прописываем команду:npm create shine-app@latest
У нас появится простая на данный момент настройка проекта, а именно указание его имени:

Дальше после того как мы введём имя приложение он создаст папку со всем, что нам нужно для реализации приложения

Структура проекта у нас выглядит следующим образом:
D:. ├───cmake # кастомные cmake функции для удобства сборки и запуска ├───frontend # Тут у нас находится фронтенд, сам UI приложения │ ├───public │ └───src │ └───assets ├───generated # Тут у нас лежат ассеты собранные в бинарник, чтобы не таскать их за собой в релизе ├───scripts # Скрипты сборки для удобства ├───shine # Код библиотеки │ ├───components │ │ ├───include │ │ │ └───shine │ │ │ └───components │ │ └───src │ └───core │ ├───include │ │ └───shine │ │ └───engine │ └───src │ └───engine │ └───win32 └───src # Код нашего приложения (в данном случае main.cpp)
На данный момент я не придумал ничего лучше, чем тянуть за собой код библиотеки в пример, чтобы избежать бед со сборкой с разными компиляторами
Дальше мы открываем проект в CLion (или там где удобно вам) и настраиваем профили CMake, У меня это выглядит так:

Дальше идем в View -> Tool Windows -> vcpkg

Далее выбираем наш существующий манифест, и нажимаем на карандашик сверху чтобы он работал с нашими CMake профилями:

Кликаем галочку Add vcpkg integration to existing CMake profiles

Далее кликаем ПКМ по корневому CMakeLists.txt и нажимаем Reload CMake Project.

Дальше мы можем спокойно запустить приложение и увидеть что оно работает:

Идею темплейта я решил взять просто с Tauri, простое окно где можно ввести имя и при нажатии Greet наше C++ ядро отформатирует сообщение и выведет его во фронтенде:

Со стороны C++ наш handler функции greet выглядит следующим образом:
SHINE_COMMAND(greet) { // У функции собранной с SHINE_COMMAND по дефолту есть аргументы // В развернутом виде функция выглядит примерно так: // nlohmann::json greet(const nlohmann::json& args) std::string name_str = args["name"]; return std::format("Hello, {}", name_str); }
и указание того, что мы можем вызвать эту функцию из фронтенда:
app.GetRouter().AddHandlers({SHINE_HANDLER(greet)});
После этого наш фронтенд знает что мы можем вызывать функцию greet с помощью метода invoke. Выглядит это следующим образом:
async function handleGreet() { if (!name.trim()) return; setIsLoading(true); try { // Вызываем функцию и записываем ответ в setGreetMsg const res = await invoke('greet', { name }); setGreetMsg(res.result || res); } catch (error) { setGreetMsg("Error: Could not connect to Shine Core"); } finally { setIsLoading(false); } }
Дальше мы можем как угодно переделывать наш проект React и делать наше UI. При сборке в дебаге приложение просто слушает локалхост с нашим портом, в Shine есть поддержка HMR и при изменении кода, UI в приложении так же изменится. При релизе же, проект фронтенда собирается и после чего переводятся в байты, дальше мы используем эти байты внутри приложения не распаковывая их храним в памяти.
Помимо всего этого у нас есть конфиг файл: shine.conf.json, выглядит он так по стандарту:
{ "window": { "title": "Shine Secure App", "width": 900, "height": 600 }, "capabilities": { "allowedCommands": [ "fs_read_text_file", "window_drag" ] } }
В нем пока что мы указываем title окна приложения, его размеры. Помимо этого есть ещё 2 параметра конфига:
frameless - Отключает рамки у окна
resizable - Разрешает / запрещает изменение размера окна
При дебаге конфиг будет подтягиваться из файла, при сборке он так же находится в итоговом приложении, опять же чтобы не тянуть за собой лишние файлы.
Учитывая что мы пишем на C++, и запихиваем ассеты фронтенда прямиком в бинарник, приложение из template имеет достаточно приятный вес - всего 845кб, при этом он не требует зависимостей для запуска и можно отправлять голый .exe пользователю и он запустится на его системе.

В планах у меня реализовать поддержку Linux и MacOS, а так же возможность получать состояние элементов фронтенда из плюсов и взаимодействия с ними. Например заполнять прогресс бар условный из C++ при какой-нибудь загрузке и т.п.
Это моя первая попытка сделать что-либо полезное на C++ для опенсорса, буду очень рад объективной критике, советам и контрибьютингу.
Комментарии (15)

Quartix
04.05.2026 15:28Спасибо за ещё одну возможность использовать браузер, чтобы просто рендерить интерфейс. Как будто нам и так веб-слопа на компе недостаточно.

ImagineTables
04.05.2026 15:28Веб-слоп делает веб-слопом не рендеринг интерфейса в браузере, а бизнес-логика десктопного приложения, написанная на джаваскрипте. Конечно, многое ещё зависит от того, какой именно взят браузер. Есть браузер, который весит 18 мегабайт и кушает меньше памяти, чем Блокнот.

Quartix
04.05.2026 15:28Под "рендерингом" я подразумевал все процессы, которые происходят на стороне браузера. Интересно, какой браузер кушает меньше, чем блокнот? В любом случае, смысла тащить web и js в десктоп - нету. Наелись уже от веб-макак. Либо учите натив, либо даже не думайте об этом.

ImagineTables
04.05.2026 15:28Наелись уже от веб-макак.
А нормальные программисты тут причём? Те, кто знают, какой браузер кушает меньше, чем Блокнот?
Макаки и среди нативщиков встречаются частенько. Не могут нативно в layout’инг, и делают большие окна non-resizable.

Quartix
04.05.2026 15:28Ну да, в этом есть правда. Но вот лично я не помню, чтобы встречал нативные приложения, у которых будут проблемы с layout. В моём окружении большинство приложений - это qt или gtk. Я чаще встречаю веб приложухи, у которых layout залочен.
А насчёт такого "браузера" - нигде такое не встречал. Если бы веб жрал меньше, то на него все и пересаживались бы. А так, его много кто использует просто потому что знает html, css и js/ts.

ImagineTables
04.05.2026 15:28Я ваши эмоции частично понимаю. Недавно я задавал вопрос, чем в CI/CD препроцессить HTML, как раз для подобных встроек (чтобы писать его с сокращениями, с прицелом на будущую генерацию на стороне юзера из DSL). Так вот, пришёл некий чел и написал, что всё это бесполезная затея, потому что Vue так всё равно не запихнуть. Эти новые программисты не понимают, как можно что-то писать без какого-нибудь Vue. И это бесит. Я, как мог вежливо, написал, что HTML != Vue, но сдержаться было трудно.
Но только вот на здоровую-то голову зачем валить? Начинается с того, что вам надоедает каждый раз писать
void OnResize() { for (int i = 0; i < _Controls.size(); i++) { … } }, и вы начинаете думать о DSL, который позволил бы этого не делать. И желательно, чтобы он поддерживал условия вида «размер этого компонента — столько-то процентов от общей ширины, но не меньше столько-то пикселей, и не больше столько-то пикселей, в случае нехватки ширины с переносом на следующую строку, в случае совсем уж нехватки ширины — с появлением горизонтального скролбара». Поздравляю, вы изобрелиflex, который позволяет писать писать это формулами вида1 1 100px. Затем вам надо добавить анимации (сворачивание, разворачивание, подсвечивание и т.п.), и вы изобретаетеtransition(не в цикле же их писать каждую по отдельности). Затем вам надо поддержать HiDPI-мониторы (4К/8К), хотя бы в форме скейлинга (как Telegram). В HTML для этого есть вёрстка вrem/em. (Вот статья, которую я об этом написал, она больше для фронтендеров, но другого способа написать адаптивные приложения (в том числе десктопные) не существует — вам надо или взять готовую реализациюrem/em, или изобрести свою, только забагованную и ни с чем не совместимую). Затем вам надо пройти сертификацию, и поддержать accessibility. Чтобы скринридер мог озвучить, где у вас меню, где тебю. Интерфейс на HTML, если вы сделали его правильно, автоматически совместим со скринридерами, а с приложением, написанным по старинке, обычно надо сделать вдвое больше работы. Потом вам надо сделать так, чтобы юзер мог работать только с клавиатуры. Потом…И вы понимаете, что написали свой браузер. (Либо написали колхозное приложение, которое не умеет в современный вид, HiDPI, accessibility и layout’инг).
18 мегабайт кушает браузер под названием Sciter. Почему его так раздуло — не знаю, пчёлы покусали, наверно )) Я им пользовался, когда он весил 5 мегабайт, и умел ВСЁ вышеперечисленное. Вот это приложение весило вместе с ним 10 мегабайт и кушало памяти… там чистые измерения нельзя провести, как вы понимаете, потому что код находится в адресном пространстве explorer.exe, но если измерить грубо, Блокнот в Windows 11 жрёт больше.
Кстати, Sciter вырос из HTMLayout, который вообще не поддерживал скрипты. Только разметку и CSS. Обработчики надо было писать на CSS+ и на C++. Тогда он весил 3 мегабайта и тормозить там было нечему.
Ultralight, про который я написал ниже, весит, конечно, побольше, но он заточен под скорость, и его запилили для геймдевелоперов. На нём предполагается писать HUD для игр. Проценты здоровья, или встроенный планшет с заданиями. Знаете, чем отличается бесплатная версия от платной? Бесплатная залочена на 60fps. Больше не даёт. Вы думаете, там что-то тормозит? А, ну ещё платная компилирует весь JS в нативный код. Хотя JS надо просто избегать.
Если бы веб жрал меньше, то на него все и пересаживались бы.
А вы думаете, легко использовать Sciter или правильно использовать Webkit/CEF? Для этого надо уметь в C++/многопоточку/маршаллинг и т.п. Или, может быть, вы думаете, что легко встраивать Ultralight в движок своей игры?

ImagineTables
04.05.2026 15:28Это моя первая попытка сделать что-либо полезное на C++ для опенсорса, буду очень рад объективной критике, советам
Ну, теперь не говорите, что не просили моего совета! ))
В этой области можно столько полезного на C++ сделать для опенсорса, а выбран был почему-то способ засунуть React в обёртку над WebView2.
Я сейчас вынужденно пилю обёртку на C++ над Ultralight. (Вам с геймдевным бэкграундом, наверно, будет интересен Ultralight). Потому, что их родная оконная обёртка (
ultralight::Window) очень тупая и не позволяет делать layered-окна. А куда же без них? Без них не сделать даже такую штуку, как простое советское контекстное меню c круглыми краями (которые я хочу задавать при помощиborder-radius). Пилить эту обёртку мне совершенно не хочется, душа просит чего-то лёгкого и воздушного, например, ваять анимации на CSS, но увы: никто другой не обернул.Если не хочется оборачивать чужие коммерческие проекты, можно аналогичным образом обернуть CEF. CEF, конечно, это не Ultralight — сплошные утечки памяти и много процессов (как и у вас сейчас из-за WebView2, кстати говоря), но возможность сделать контекстное меню c круглыми краями и блуром прямо на CSS — это всё равно круто и мегаполезно. (Для этого надо вписывать результаты рендеринга CEF в виндовый композитинг через буфер кадров — и направлять ввод в его псевдоокно).
Если не хочется брать CEF, то вот тут @drakkonne хвалит WebKit.
А React или ванилин — это уж второстепенной важности вопрос. Тем более, люди, которым нужен React, скорее всего, всё равно возьмут React Native или что-нибудь похожее. Или просто Electron.

wtf-keaton Автор
04.05.2026 15:28В этой области можно столько полезного на C++ сделать для опенсорса, а выбран был почему-то способ засунуть React в обёртку над WebView2.
Честно - делал изначально для себя, но получилось вроде бы даже на столько прикольно что выпустил в опенсорс
Без них не сделать даже такую штуку, как простое советское контекстное меню c круглыми краями (которые я хочу задавать при помощи
border-radius). Пилить эту обёртку мне совершенно не хочется, душа просит чего-то лёгкого и воздушного, например, ваять анимации на CSS, но увы: никто другой не обернул.Прямо сейчас я как раз балуюсь с этим в рамках Shine (хотя щас переключился на то, чтобы написать тесты и бенчмарк). CEF не хочу использовать, у меня к нему негативный опыт работы, а на счет webkit почитаю посмотрю, спасибо)

yarrrman
04.05.2026 15:28Есть же WebviewUI. И там поддержка кучи бакендов от С и Pascal до Odin и Zig
У tauri плюс в том, что на нем еще и мобильные приложения можно делать
Tyiler
Смотрим код
Смотрим что такое WebView2
В общем, в статье речь не о том, что надо бы осветить.
wtf-keaton Автор
От электрона я отказался не из за использования WebView, это меня как раз таки устроило. Меня не устроил размер и количество файлов на выходе, а так же то что мне не особо понравилось как мне реализовывать там модули на C++, а это был важный критерий для меня.
В случае с Shine я могу реализовать фунцкию обернув её в SHINE_COMMAND макрос и спокойно вызывать из React интерфейса
Tyiler
вот и надо было про внутр кухню больше написать, а не скрины делать где какой флажек нажать, чтобы собрать в clion.
про память на жест диске. не на то вы внимание обращаете совсем, важнее сколько опер памяти берет, а брать будет как и электрон, скорее всего.
wtf-keaton Автор
Нет ОЗУ берет меньше. Но спасибо за замечание. Мой первый опыт что то писать на Хабре. Учту