| 2026-10-06 |
webclient: LoginScreen rebuilt on kit markup
...
The SSO-only login doesn't fit GnLoginCard's username/password form (the
adapter always renders both fields), so the escape-hatch card is replaced
with the kit's own login-card markup (article.card.login-card + header +
card-content + GnButton) — the same contract kit.css ships for
GnLoginCard, minus the form. Custom card border/title/subtitle styles are
gone; overlay keeps positioning only.
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: apply kit radii to content-cards and viewed-image thumbs, fix dead scss tokens
...
The content-card and ArtifactsPanel style blocks used @use 'kit-deps'
inside <style scoped> without lang=scss, so sass never ran: the
-radius-md token stayed a literal in the built CSS and the
browser saw border-radius: -radius-md (invalid → 0 corners).
Both blocks now declare lang=scss and compile to the kit's 6px radius.
Also rounds the corners of user-message image attachments and adds the
missing label prop on the tools-collapse icon button (kit dev warning).
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: cap thinking body at 800px, smooth details close, Navi-viewed images grouped in artifacts
...
- thinking-body: max-height 800px + inner scroll so long reasoning
doesn't grow the whole chat
- content-card: body stays mounted after first open (no v-show teardown
at toggle time) — ::details-content now has real content to shrink,
so collapsing animates smoothly instead of snapping to 0 in a frame
- synthetic '[Image loaded via ...]' messages from vision tools are no
longer rendered as user bubbles; their images are collected (live from
tool_call metadata, and from history) into a viewed-images group at
the top of the artifacts tab, thumbnails open the lightbox
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: tool-count badge, kit-styled content card, thinking corner gap, chat top gap, drop duplicate reconnect notice
Eugene Sukhodolskiy
committed
1 day ago
|
webclient + navi: session title click-to-rename; empty rename regenerates via LLM
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: profile chips — wheel + swipe scroll, no status markers, clicked chip slides into view
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: profile chips in sidebar; New Chat opens welcome; profile tap starts session; welcome grid 4-up
Eugene Sukhodolskiy
committed
1 day ago
|
webclient: lightbox closes on browser/back-gesture
...
Opening the lightbox pushes a history entry ({lightbox: true}); popstate
closes the modal instead of walking the session history (mobile back
gesture used to leave the modal open and jump sessions). Cross/backdrop
close pops the pushed entry, so the gesture history has no stale records
Eugene Sukhodolskiy
committed
1 day ago
|
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: W5 — icon/variant audit + design-system docs in CLAUDE.md
...
- ApiKeysPanel: dropped invalid variant="ghost" on GnIconButton (the
component has no variant prop — it leaked into the DOM as an attribute)
- Icon audit: all icon props/classes carry the required ph base class
and ph- prefix (kit v1.0 dev-warning otherwise); variant whitelist
(primary/secondary/warning/danger/info) confirmed against the kit
- webclient/CLAUDE.md: new 'Design system (gnexus-ui-kit v1.0)' section
— points to vendor/gnexus-ui-kit/CLAUDE.md, documents prebuilt-css
consumption, dark kit.css theme, --gn-* + kit-deps tokens, icon rule,
test-locked app classes, and the documented non-kit surfaces
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: W4 — chat empty state -> GnEmptyState, card modal -> GnModal
...
- ChatArea: .empty-chat custom icon+text -> GnEmptyState card
(keep .empty-chat wrapper only for centering)
- CardGrid: inline Teleport modal (overlay/close/scroll bespoke chrome
removed) -> GnModal; .card-modal/.card-modal-title class names kept
inside the new DOM (test contract); kit header slot renders the title
- WelcomeScreen: typography aligned to kit (30px h1, 14px base),
profile cards get -radius-md, dead .profile-icon style dropped
- CardGrid test: mount with the kit plugin (GnModal resolves),
two nextTicks for the post-flush open watcher
Verified: vitest 111 passed, build OK, welcome screenshot unchanged
(modal/empty-state visually checked after final build)
Eugene Sukhodolskiy
committed
2 days ago
|
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
2 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
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: session-list polish from review pass
...
- documentTitle: persistent nameById map — names for sessions beyond the
loaded list page (list only holds ~30); map filled by fetches and
loadSession meta, never wiped by later refetches
- createSession: placeholder inserted at the position the server list
would give it (after pinned run, last_active desc) so refetch no
longer visibly moves the new row
- SessionItem: skip empty .session-icons wrapper when no icons
- drawer breakpoint made exclusive (-sidebar-drawer 1280 -> 1279.98):
viewport of exactly 1280px now gets the desktop sidebar
Eugene Sukhodolskiy
committed
2 days ago
|
webclient: fix session-list flicker and double-load on select
...
- fetchSessions merges into existing items (preserve object references)
instead of wholesale array replacement — DynamicScroller reuses rows,
the list no longer repaints/reflows on every refetch (measured: sidebar
repaint spikes 8-17% per switch -> 0)
- AppSidebar.handleSelect no longer refetches the list on a plain select;
refetch only when leaving search (the array still held search results)
- loadSession now lives only in handleSelect — SessionList.onSelect used
to start a first load and handleSelect raced it with a second one
(double fetch + double buildMessageList, incl. search-jump target)
- pinSession mutates in place instead of replacing every item object
- SessionItem.highlightText matches on the raw string so <mark> offsets
stay correct when the text contains &/</>
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
|