| 2026-10-09 |

webclient: the settings screen in en, uk and ru
...
Settings is the first area off hard-coded English: 122 strings for the shell and
its five tabs — App, Account, Notifications, Synapse and MCP — plus the 43 the
shared vocabulary contributes (common.*, col.*, confirm.*, toast.*).
The English dictionary carries the very literals the templates used, so the
interface a test reads is byte-for-byte what it was and the 56 assertions on
English UI text stay green; only a slug that is genuinely counted had to be
restructured into a plural. In ru and uk the counted strings get the three
Slavic forms driven by n. Product names are never translated: Navi, Synapse,
MCP, Ollama, Tool call, Subagent and Token read the same in all three
languages, and the settings screen keeps one settings.* area with
panel-prefixed slugs rather than a slug per panel, as the other gnexus clients
do.
Strings carrying markup are split around it instead of hiding tags in the
dictionary: the install steps keep their inline icons and <code>http://</code>
in the template, with the words of each sentence on either side. Four lists of
labels — the settings tabs, two table headers, and the reaction options — are
now computed rather than module-level constants, since a list built at import
keeps the language that was active then and ignores a switch. Dates in the
panels go through the interface language as well, not the browser's.
Checked in Chromium against the built bundle: all three languages on all five
tabs, with a scan for a slug leaking into the rendered text as the pass
condition, and the account left with no override afterwards.
Eugene Sukhodolskiy
committed
1 day ago
|
| 2026-10-07 |

Android app: native push notifications via JS bridge + background hold
...
WebView has no Push API, so the app shows notifications natively:
- NaviBridge JS interface (window.NaviAndroid): notify(), permission
request with a 'navi-perm-result' event callback, background-mode
start/stop and the Doze-exemption ask.
- BackgroundService: dataSync foreground service + partial wake lock —
holds the process (and the WebView WebSocket) open while the app is
minimized; quiet persistent notification with a "Отключить фон" action.
Notification channels: silent background presence, high-importance
messages.
- Manifest: POST_NOTIFICATIONS / FOREGROUND_SERVICE(_DATA_SYNC) /
WAKE_LOCK / REQUEST_IGNORE_BATTERY_OPTIMIZATIONS + service entry;
notification tap resumes MainActivity (singleTask) into open_url.
- webclient: nativeBridge composable; usePush gets a bridge mode that
replaces web-push entirely (settings toggle keeps working); chat store
raises 'Navi ответила' through the bridge on stream end when the
window is hidden. Fixed a latent undefined-variable in web syncState
(notificationsSupported -> notifSupported()).
- New unit tests for the bridge surface; 147 frontend tests + APK build
pass.
Eugene Sukhodolskiy
committed
3 days ago
|
| 2026-10-06 |
webclient: mobile input bar flush; settings panels full container width
...
- .input-bar on mobile: no top/bottom padding — once attachments appear,
the file strip provides the gaps itself
- ApiKeys / Notifications panels: drop the 800px cap, fill the centered
1200px settings column
Eugene Sukhodolskiy
committed
4 days ago
|
| 2026-10-05 |
webclient: W3 — migrate settings/sidebar/chip surfaces onto kit components
...
- AppSidebar: hand-styled <select> -> GnSelect (options computed from the
profiles store); the ~60-line bespoke select styling in app.scss is now
three compact overrides collapsing GnSelect's form chrome for the slim
sidebar row
- ApiKeysPanel: bespoke grid-table -> GnTable with cell slots (monospace
key prefix, formatted dates, revoke GnIconButton in actions); loading
state -> GnLoader; no-auth callout -> GnAlert(variant=warning)
- NotificationSettingsPanel: unsupported-browser callout -> GnAlert(info)
- SettingsView: header -> GnPageHeader (compact) with sidebar toggle in
#actions
- ShowTokenModal: token field -> GnInputGroup + GnCopyButton (drops the
bespoke copy/useCopy wiring)
- QueuedMessagesChip: .task-chip div -> GnChip (icon prop)
Verified: vitest 111 passed, build OK, screenshots of / and #settings
Eugene Sukhodolskiy
committed
5 days ago
|

webclient: retokenize styles onto gn-ui-kit v1.0 CSS variables
...
All ~165 legacy var() usages (--color-*, --surface*, --accent*, --border,
--text*) with hex fallbacks and ~150 raw hex literals across app.scss and
24 scoped style blocks are swapped to the kit's --gn-* tokens per the
intent of the old fallback values:
- accent/primary/teal (#7aa2f7/#4ec9b0) -> --gn-color-secondary
- amber/orange highlights -> --gn-color-accent
- text greys -> --gn-color-text-{light,medium,dark}
- panels -> --gn-surface-panel; elevated tints -> color-mix over panel
- translucent overlays -> color-mix(in srgb, ..., transparent)
Deliberately literal: terminal deep bg #0f0f14 (darker than any token)
and the content-card image badge teal #89dceb (kit has no teal). File
type badges keep a categorical palette via nearest kit tokens.
Dead "UI kit overrides" block halved: kit 1.0 dropped hover icon
rotation, so only the app-owned chevron open-state flip remains.
Verified: vitest 111 passed, build OK, dev render matches W1 baseline
Eugene Sukhodolskiy
committed
5 days ago
|
| 2026-09-26 |

PWA: installable webclient, offline shell, web push
...
Installability:
- public/manifest.webmanifest (standalone, theme #16161E) + PNG icons
generated from logo.svg (regular + maskable, served via /images mount)
- index.html: manifest link, theme-color, apple-touch-icon
Offline shell:
- hand-rolled sw.js (no workbox): navigation = network-first (3s race)
with cached-shell fallback + background refresh (a stale cached shell
would 404 on entry chunks after a deploy); /assets/* cache-first
(content-hashed); /images/* cache-first capped; /api,/ws,/auth,/push,
/content pass-through
- vite closeBundle plugin stamps __NAVI_BUILD_VERSION__ (digest of
index.html + asset names) into dist/sw.js; sw.js served no-store so
every deploy reactivates the SW and activation evicts old caches
- SW registration in main.js, PROD only (dev HMR untouched)
- OfflineBanner (useOnline composable) over the app shell
Web push (VAPID, pywebpush):
- navi/push/ package: push_subscriptions table (postgres, boot-time DDL),
PushSubscriptionStore, PushService (async fan-out, to_thread sends,
404/410 prunes dead endpoints, per-session cooldown)
- routes: GET /push/vapid-key, POST/DELETE /push/subscribe (auth-gated)
- trigger in orchestrator run_agent + run_recall: push on StreamEnd when
no WebSocket client watches the session; fire-and-forget, never
disturbs the run; anonymous fallback only when auth is off
- client: usePush composable + Notifications settings panel (enable/
disable via PushManager.subscribe with the server VAPID key)
- notification click focuses the app at /#<session_id> (hash routing
opens the right chat); payload body is a markdown-stripped <=140-char
preview
NAVIVAPID keys empty = push fully disabled (graceful, like other optional
integrations). dist/ artifacts committed per repo convention.
Tests: pytest push store/service/routes/trigger (+23), vitest usePush
(83 webclient tests green). Full suite 1143 passed.
Eugene Sukhodolskiy
committed
14 days ago
|