| 2026-10-06 |
webclient: artifacts panel — link hover color, single-row tabs, refresh moved right
...
- .link-item hover/active title: text-light instead of black (unreadsble
on the row hover tint)
- tab chips never wrap: container queries on the panel squeeze padding/
font/gap and drop the icons below 360px
- files refresh: default-size GnIconButton aligned to the toolbar right
Eugene Sukhodolskiy
committed
1 day ago
|
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
1 day ago
|
webclient: artifacts panel — compact actions, refresh in static row, file icons via phosphor 2.x
...
- artifact-actions: flex wrap gap 4px instead of full-width stretched grid
- files refresh: static toolbar row (GnIconButton sm) instead of floating
absolute button that sat on top of the first list rows
- file icons: phosphor vendored 1.4.2 lacked the file-type glyphs
(py/ts/js/html/css...) that fileIcon() already mapped — upgrade to
@phosphor-icons/web 2.1.2 (bundled regular font), drop the vendor dir;
json -> file-code (phosphor has no json glyph)
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: polish batch — caps off, kit badge/chip in header+input, search clear closes, mobile full-bleed input, settings centered
...
- chat-header-title and sidebar logo span: remove forced uppercase
- profile badge -> GnBadge (variant per profile: secretary secondary,
server_admin warning, developer info); custom box styles dropped
- pending file pill -> GnChip (icon + removable); image thumb stays
app-owned (no kit image-preview component) but on kit surface look
- sidebar search clear button now also closes the field
- input-row: no yellow focus ring; on mobile full-screen width, no
borders, inner padding carries the breathing room
- sidebar footer: without-hover removed from gear/admin/logout buttons
— kit hover returns
- settings: header and body centered, max-width 1200px
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: artifacts panel -> kit 1.0 visible pass
...
Tabs became kit chips (aria-pressed selected state, plain count inside),
empty states became GnEmptyState, action/refresh/close icon buttons moved
to kit btn-icon / GnIconButton, list badges to kit .badge variants. List
rows dropped the filled-box look for the kit idiom: subtle hover fill and
a left accent bar with panel-muted fill for the selected row. Header is
now stretchable so wrapped chips don't clip. Raw hexes and font literals
replaced with --gn-* tokens.
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: center lightbox photo in the modal panel
...
Kit's .modal .modal-dialog .modal-panel min-height rule (0,3,0) beat the
lightbox's own override, so shorter photos sat at the top of an empty
panel and the dialog hugged them horizontally. Drop the shrink-wrap, let
the dialog keep the standard kit width and center the image in the panel
body both ways.
Eugene Sukhodolskiy
committed
1 day ago
|
| 2026-10-05 |
webclient: replace vue-virtual-scroller with plain content-visibility rendering
...
The virtual scroller's measurement states duplicated item views (parked
copies at translateY(-9999) alongside working ones), which made tool/plan/
thinking cards unclickable and the chat flicker. Both chat messages and
the session sidebar are now plain v-for lists; off-screen rows skip
render/paint via content-visibility: auto, keeping long sessions cheap
without the scroller engine. Library removed from dependencies.
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: reconnect banner -> GnAlert (kit warning styling)
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: V1–V3 — visible kit 1.0 pass on input bar, sidebar and chat cards
...
- Input row shaped exactly like a kit input (panel-muted, 2px muted
border, 3px radius, hover/focus-within states)
- Sidebar: uppercase NAVI logo, row separators, session names in
text-light, uppercase chat header title
- Chat surfaces: 6px radius on thinking/plan/tool/subagent/summary
cards, summary card switched to muted border + info accent stripe,
user bubble radius, pill radius on file pills, kit radii on
code-block and markdown image links, 6px accent quote stripe in
tool results
- dist rebuilt on kit 1.0
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: product dist rebuilt on kit 1.0 (tracked, served by backend)
Eugene Sukhodolskiy
committed
2 days ago
|

webclient: refresh vendored gnexus-ui-kit to v1.0.0 + dist-css wiring
...
- vendor 0.2.0 (snapshot d640a9e, ~93M with demo/node_modules) replaced
with a clean 1.0.0 subset (4.5M): src/{js,scss,vue}, dist/{css,vue,js},
dist assets (IBM Plex Mono fonts), docs, README/CLAUDE/AGENTS
- kit component CSS now consumed as prebuilt dist (import
'gnexus-ui-kit/css' before app styles): kit 1.0 _fonts.scss uses
relative url() for IBM Plex Mono, which breaks source compilation;
the bundler resolves them into vendor dist/assets
- main.scss drops @use 'kit' as * (still broken by the font urls);
kit-deps stays for focus_ring + SCSS tokens in app.scss and scoped
blocks
- app.scss scrollbar block removed (kit.css 1.0 ships identical styles)
- vite.config: dead @kit alias removed; brand-new 'gnexus-ui-kit/css'
alias now consumed
- product dist/ rebuilt (tracked: served by backend, deployed from git)
Verified: vitest 111 passed, vite build OK (ttf emitted, hashed),
preview screenshot renders dark kit theme with new density
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: merge Terminals into Backgrounds tab (single list)
...
One list of background tasks and terminals, running first. Row icon and
badge distinguish kind; details route to task or terminal card. Selection
resets on session switch and drops dangling entries when the selected
task/terminal disappears.
Eugene Sukhodolskiy
committed
2 days ago
|

webclient: Backgrounds tab in artifacts panel + task toasts
...
- new Backgrounds tab: task list (running first, badges, per-tool icons)
with a detail view (status, timestamps, sub-agent tokens, result/progress)
- GET /sessions/{id}/tasks snapshot endpoint: task_update events are not
replayed on reconnect, so the client fetches the task list on session
load/reload; live task_update entries are merged on top (chat.fetchTasks)
- terminal task_update no longer removes the entry — it marks it finished
so the tab shows recent completions; _terminalTaskIds still blocks
resurrection by a late running update
- toasts for background task start / finish (info / success / error) in
the WS dispatch; silent for other sessions and unknown terminal ids
- persistent background-tasks chip removed (replaced by the tab); only
the queued-messages chip stays
- fix invisible status text in terminal detail rows: filled .status-*
backgrounds now scope to .terminal-status-badge only
Eugene Sukhodolskiy
committed
2 days ago
|

background tasks: review fixes B1-B11 batch
...
- stop mid-batch cancels in-flight tools (B1) and keeps real results
of already-finished ones, mixing them with synthetic stopped notes
in call order (B2)
- queued messages: headless drain publishes session_sync (B4), the
run's teardown broadcasts session_sync to other sockets but not the
owner socket (prevents double reload) (B5)
- task_update notifications are chained per task so late running
updates can't overtake the terminal one (B7; client drops late
re-flicker of a terminal task chip) (B8)
- client: ui_component results reference the owning message via
card.parentMsg instead of a stale msg reference (B6)
- tasks cancel reports the real outcome after a bounded wait instead
of an optimistic 'cancelled' (B9)
- session delete cancels its running background jobs and drops
pending result notes (B10)
- subagent tool loop routes background:true calls through
ToolExecutor._maybe_background like the main loop (B11)
- tests for all of the above; docs/tasks.md stop/cancel semantics
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: virtualized message list (DynamicScroller)
...
- MessageList rewritten on vue-virtual-scroller DynamicScroller; only rows
near the viewport mount (session switch 650-730ms longtask / DOM 31645 ->
110-165ms / DOM 1-2.5k, zero freezes)
- scroller is keyed by session id: message ids repeat across sessions, so
teardown drops the measured-size cache on switch
- convergeToBottom watches the bottom hold for a bounded window after
landing (lazy row measurements nudge scrollHeight late); yields to wheel/
pointer/touch input instead of scroll-position heuristics, which the
scroller's own adjustments would trip
- renderMarkdown gets a byte-capped LRU memo so markdown remount cost is
amortized while virtualized rows recycle
- removed dead .message-list/.message-list-inner styles
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: loading indicators where the UI looked frozen
...
- session open: centered spinner in MessageList while chat.loading
- image paste/drop: imagesProcessing counter (chat store, shared);
FilePreviewStrip shows spinner placeholder tiles where the resized
previews will land
- file upload: indeterminate sliding bar instead of the fake static 60%
- send → run start gap: "Sending…" hint with spinner, cleared on
stream_start / message_queued / stream end / error / session switch
- content-card images: skeleton with spinner until @load (reset on src
change); wrapper keeps min-height while the img is empty
vitest 96 passed.
Eugene Sukhodolskiy
committed
2 days ago
|

chat history pagination: paged load instead of full-session fetch
...
Backend:
- PgSessionStore.get_meta — light session head (sessions row + COUNT),
no message payload
- PgSessionStore.get_history_page — page of display history (archive ∪
hot UNION), newest-first page, oldest-first result, limit+1 peek for
has_more; each message stamped metadata.display_index (ROW_NUMBER-1
over the full display history) so client ids stay stable across
paged loads; dangling-tool-call repair keeps real ranks by
sequence-number lookup
- GET /sessions/{id}/meta and GET /sessions/{id}/messages/history
(before_seq cursor, limit ≤200)
Webclient:
- loadSession/reloadSession fetch meta + newest 200-item page
(Promise.all), archive state from the page itself
- buildMessageList ids (h_*) and rawIndices use the global
display_index — feedback keys and search-jump stay stable when
history is paged
- scroll-up loads older pages through the same history endpoint
(archive table alone misses sessions whose threshold never moved)
- search-jump pulls pages until the target index is covered (bounded)
Tests: store unit tests (ranks, has_more peek, placeholder shift) +
vitest updates; full pytest 1231 passed, vitest 94 passed.
Eugene Sukhodolskiy
committed
2 days ago
|
| 2026-09-26 |
webclient: background task chip, id-first tool matching, queued badge
...
- toolIndex (tool_call_id → card): exact matching for parallel batches and
late events after stream_end; legacy name+pending fallback kept
- bare tool_call synthesizes a card so results are never lost
- task_update: backgroundTasks chip above composer + steps appended to the
spawning tool card (parent_tool_call_id binding)
- message_queued badge, terminal_opened dispatch
- new BackgroundTasksChip component, task_update step rendering in ToolCard
Eugene Sukhodolskiy
committed
11 days ago
|
webclient: lightbox dialog shrink-wraps the photo, img without crop
Eugene Sukhodolskiy
committed
11 days ago
|
webclient: code blocks span full message width in flex markdown container
Eugene Sukhodolskiy
committed
11 days ago
|

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
11 days ago
|