Catalog · How the catalog works
GnauralCore
Anyone may press Interesting, signed in or not. It helps others find this project.
README
GnauralCore
Owned location for the extracted audio core product.
Current ownership:
cli- product-local Pascal audio vendor tree under
cli/vendor/uos - product-local Gnaural control protocol under
cli/common server- audio UI modules from
ui
No compatibility shim remains under pas/gnaural.
Public entry points (since GCY4.1, plan gnauralcity, GCY-D14):
- Hosts and other modules import this component by package name, only through the entries declared in
exports— never a deep path. Inside the component its own files are imported by relative path. @gnauralcore/ui(ui/package.json):.(gnauralModule,createGnauralPlugin),./module,./plugin(also theMenuNodetype),./widgets(useAudioStore,useAudioTransport,loadGnauralScheduleView,loadGnauralTransportControls— embedded audio widgets, GCY-D20),./protocol(the browser↔server protocol, for browser and server alike),./i18n.@gnauralcore/server(server/package.json):.(server/index.ts—createGnauralServerModuleand its API/host types, the settings store,checkNewProjectDefaults) and./testing(server/testing.ts— for host tests only; production code never imports it).- The repo root
package.jsonis@gnauralcore/core— scripts only. - A host UI lists
ui/inhostUi.modulePackages(HostKitCore's factory turnsexportsinto exact aliases andpaths); a host server maps the names withpathsin itsserver/tsconfig.json. A new need is a new entry here: a newuientry needs every host's generatedtsconfig.hostkit.jsonregenerated (validate-qa.jsreddens until it is), a newserverentry apathskey in the server tsconfig of each host that uses it.
UI composition contract:
ui/package.jsondeclares the source-level public entrypoints used by host products.- The package root stays minimal and re-exports only
gnauralModuleandcreateGnauralPlugin; everything else goes through the declared subpath exports. uiexportsgnauralModulefor route, settings-tab, and locale-message contributions.uiexportscreateGnauralPluginfor runtime wiring; the host must provide the sharedwsadapter before rendering Gnaural pages.- The host also passes the shell's naming function,
createGnauralPlugin({ ws, hostKey })(hostKeyfrom@hostkit/app/host-keys; GCY4.3, GCY-D22): every localStorage key of the component's view is<host prefix><name>, built byui/view-keys.tsat the moment of use — one prefix per host,configureHostKeys({ keyPrefix }), the same that names the panel framework's keys and the language key (MindWavemindwave-, its saved view stays in place; Gnauralcitygnauralcity-). A new view key is added toVIEW_KEY_NAMESthere; the lockui/view-keys.test.tsholds MindWave's names byte for byte. - Gnaural owns its module-local navigation namespace, including
nav.audio. - Host shells such as MindWaveCore own only shell concerns like top-level navigation, archive/log pages, and app bootstrap.
Расширение компонента хостом
Хост (продукт из hosts.json) подключает компонент двумя вызовами — UI-плагином и серверным модулем — и всё, чем продукты различаются, передаёт их опциями (GCY-D7, GCY-D18; план gnauralcity). Ветвлений «если это MindWave» в компоненте нет, имён продукта он не знает (GCY-D19). Эталон — Gnauralcity: всё, что нужно ему, и есть обязательная часть контракта (GCY-D13); всё сверх — формы ниже.
UI — createGnauralPlugin(options) (@gnauralcore/ui/plugin)
| Опция | Что это | Хосты |
|---|---|---|
ws (обязательна) |
ws-транспорт оболочки — wsService из @hostkit/ws/ws. Плагин сам регистрирует на нём обработчик audio_*, отвечаемые типы часов живости и префиксный канал spectrogram:; порядок установки плагинов хоста — порядок раздачи событий (MindWave ставит этот плагин первым, MindWaveCore/ui/src/boot/module-plugins.ts). |
оба |
hostKey (обязательна) |
Функция имён оболочки (@hostkit/app/host-keys, GCY4.3, GCY-D22): каждый ключ localStorage вида компонента — <префикс хоста><имя>; префикс один на хост — configureHostKeys({ keyPrefix }). |
MindWave mindwave-, Gnauralcity gnauralcity- |
menuItems (необязательна) |
Пункты «Меню» AudioPage (форма «пункты меню», GCY-D18, GCY2.2) — readonly MenuNode[] (тип отдаёт тот же вход). Встают после разделов редактора, перед «Настройками» и «Выходом». |
Gnauralcity — язык и «Bun-действия» (Gnauralcity/ui/src/boot/host.ts); MindWave не передаёт |
Правила пунктов меню: подпись labelKey — ключ i18n приложения (сообщения хоста или оболочки; модуль их не знает); id — в пространстве имён хоста host.* (меню ищет листья по id и по нему ведёт список недавних — совпадение с командой модуля спутало бы их); подменю — children, действие листа — run, недоступность — disabled; служебный пункт помечается noMru: true у каждого листа (список недавних читает поле листа, а не раздела).
Ещё хост берёт: gnauralModule (@gnauralcore/ui/module — маршрут audio, отсоединяемые панели, сообщения i18n) — в свой реестр модулей (assembleModules, маршруты — moduleRouteRecords HostKitCore); app.title и свои ключи i18n хост даёт сам (assembleAppMessages). Встраиваемые виджеты — вход @gnauralcore/ui/widgets (GCY-D20: транспорт, вид расписания, стор звука — живое состояние единственного плеера приложения).
Сервер — createGnauralServerModule(host) (@gnauralcore/server)
Что модуль получает от хоста (GnauralServerHost, GCY-D25):
| Поле | Что это | MindWave / Gnauralcity |
|---|---|---|
runtimeDir |
Каталог рантайма (GCY-D26): там var/gnaural-settings.json, если хост не дал settings. |
server/ или HK_RUNTIME_DIR — у обоих |
diagnosticsDir |
Каталог диагностических логов (ui-diag.log, fs-browser.log, undo-log-perf.log) — отдельно от рантайма: песочница подменяет рантайм, а след остаётся там, где его ждут читатели (GCY-D26). |
свой server/var — у обоих |
userDataRootName |
Имя корня пользовательских данных %LOCALAPPDATA%\<имя> (GCY-D27); кэш следует за корнем. |
KKSoundCore / Gnauralcity |
writeFenceRoot |
Заборчик записи песочницы ("" — нет). |
HK_WRITE_FENCE — у обоих |
dev |
Дев-режим: серверная половина журнального следа горит безусловно (AGENTS.md, «Журнальный след»). | HK_DEV=1 (bun run dev) — у обоих |
publish |
Публикация текста события в ws-тему оболочки "ui"; зовётся только после старта сервера. |
server.publish(...) каркаса |
settings (необязательно) |
Хранилище настроек компонента, УЖЕ перенесённое хостом (GCY-D24); без него модуль заводит своё в runtimeDir. |
MindWave — после однократного переноса из своей базы журнала; Gnauralcity не передаёт |
Окружения хоста компонент не читает (только системное LOCALAPPDATA — корень данных по умолчанию, собственные пути к exe GNAURAL_EXE, SPECTRUMCORE_WORKER_EXE и флаги своих тестов): значения выше хост читает сам, функцией readHostEnv HostKitCore (HostKitCore/server/host-env.ts) под нейтральными именами HK_* (GCY-D38). Прежние MW_* MindWave сняты в GCY6.1 и не читаются; заданное старое имя даёт строку [host-env] … больше не читается в консоли сервера MindWave.
Что модуль возвращает (GnauralServerModule): модуль каркаса HostKitCore — fetch своих путей (/api/audio/*, /api/projects*, /api/project-settings, /api/new-project-defaults, /api/fs/info, /api/ui-diag; чужой путь /api модуль не отвечает — хвост 404 у каркаса), ws (заявка на свои audio_* и spectrogram:* по форме сообщения, audio_status на открытие сокета, уборка на закрытие), dispose (движок, вотчер расписания, сервер файлового браузера); плюс api, fsBrowserUrl (для строки в консоли хоста) и startBackgroundTasks() (позвать после старта).
Публичный API звука (api, GnauralServerApi) — программное управление и события (форма GCY-D18): beginStart, start, stop, pause, resume, seek, getStatus, getLoadedSchedule, getLoadedScheduleStartedAtMs, getAccessRoots; подписки onScheduleLoaded(listener) — сырое событие загрузки расписания, синхронно, до публикации клиентам (MindWave пишет по нему архив сеансов), и onTransportCommand(listener) — "start"/"stop" клиентских audio_start/audio_stop по ws, синхронно, до самого действия; вызовы API — не команды (MindWave по ним отпускает временный файл реплея). Обе возвращают отписку; исключение слушателя не гасится.
Свои ручки и ws хост добавляет не в модуль, а рядом: своим модулем в списке modules каркаса (startHostServer, HostKitCore). Модуль без claims получает всё незаявленное (у MindWave — его модуль хоста: ЭЭГ-режимы поверх api, реплей, BodyMonitor, архив); порядок списка — порядок open и цепочки останова.
Секции проекта хоста
Состояние, привязанное к открытому файлу, — секция проекта (AGENTS.md, «Per-file data»; руководство и таблица имён — MindWaveCore/docs/project-store/project-store-api.md). Имена секций компонента перечислены в коде — ui/project-sections.ts, по строке назначения на имя; замок server/project-sections.test.ts держит, что код читает и пишет ровно их и что ни в одном нет -, а validate-qa.js — что таблица руководства та же. Решение владельца 2026-10-16 («Префикс хоста + вход»; GCY-D18, GCY-D19): секция хоста называется <префикс ключей хоста><имя> — тот же префикс, что у ключей вида (mindwave-…, gnauralcity-…, GCY4.3); дефис в именах компонента запрещён, поэтому пространства имён не пересекаются. Читает и пишет свои секции хост через НОВЫЙ вход компонента, который ставит префикс сам; вход заводится вместе с первой секцией хоста, не раньше. До него ни один вход клиентского API секций (readProjectSectionFor/writeProjectSectionFor) не отдаёт, и хост секций не пишет.
Чего хост не может
- Импортировать файл компонента мимо входов
exports(@gnauralcore/ui,@gnauralcore/server) — глубокий путь, алиас или копия файла в хосте; входtesting— только из тестов. Это держит замок контрактаvalidate-qa.js(GCY4.2) на каждом шаге и ночью. Нужно то, чего ни один вход не даёт, — новый вход вexports(см. выше). - Ждать от компонента знания о себе: никаких веток по хосту, имён продукта и переменных окружения хоста внутри компонента; различие — только значением опции.
- Отвечать на сообщения компонента своим ws-модулем раньше компонента: заявки раздаются в порядке списка модулей, и ws-модуль хоста заявок на
audio_*/spectrogram:*не делает.

