Chat-first layout with formal-ai UX, all engines, and Links Notation storage (issue #15)#16
Conversation
Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: #15
…stence Add a dependency-free Links Notation parser/serializer and a conversation data model that persists all chat data to localStorage as a portable .lino bundle (mirroring formal-ai's storage format), with export/import support. Includes 11 unit tests covering round-trips, escaping and forgiving imports.
…es, and Links Notation storage (issue #15) - Replace the catalogue-first single column (which pushed chat off-screen) with a three-panel shell: fixed sidebar (brand, conversations, engine picker, model catalogue, export/import) beside an always-visible chat column. - Add FormalAiProvider as the default chat engine, mirroring formal-ai UX. - Wire all six chat engines from react-chat-ui (formal-ai, chatscope, deep-chat, react-chat-elements, assistant-ui, reachat) as selectable. - Persist conversations to localStorage as a Links Notation (.lino) bundle with sidebar export/import. - Fix the reachat narrow-selector bug structurally (selector in its own column) plus defensive min-width/width CSS.
…inference selectors (issue #15)
Working session summaryEverything is complete. Here's the final summary. Issue #15 — Done ✅The bug: Selecting a chat engine + model only downloaded the model — there was no way to actually chat. Root cause was purely layout: the model catalogue rendered as a full-width grid that filled the viewport and pushed the chat composer off-screen. For The fix (PR #16, now marked ready):
Verification (all green):
Housekeeping: changelog fragment added ( PR: #16 This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $14.742046📊 Context and tokens usage:Claude Opus 4.8: (4 sub-sessions)
Total: (52.9K new + 525.5K cache writes + 14.1M cache reads) input tokens, 148.2K output tokens, $14.304322 cost Claude Haiku 4.5:
Total: (38.5K new + 112.2K cache writes + 1.8M cache reads) input tokens, 16.5K output tokens, $0.437724 cost 🤖 Models used:
📎 Log file uploaded as Gist (8064KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
🎉 Auto-mergedThis pull request has been automatically merged by hive-mind.
Auto-merged by hive-mind with --auto-merge flag |
Summary
Fixes #15. Selecting a chat engine + model only downloaded the model — there was no way to actually chat. Root cause: the model catalogue rendered as a full-width grid that filled the viewport and pushed the chat composer off-screen. For the
reachatengine the shared column additionally squeezed the model selector narrow.This PR makes the app chat-first with a persistent three-panel layout and brings the chat experience to parity with formal-ai: a conversations panel, Links Notation storage, export/import, and every react-chat-ui engine selectable.
Requirements
FormalAiProviderdefault — avatars, copy, markdown, auto-growing composerlocalStorage;.linoexport/importmodel_in_browser_bundle.linodocumente2e/inference.spec.ts+e2e/chat-ux.spec.tsdocs/case-studies/issue-15Root cause
The pre-fix
App.tsxrendered a single vertical column with theModelSelectorgrid (~18 cards) at the top. The grid is taller than the viewport, so the chat surface below it scrolled out of view — selecting a model "did nothing" visible except download. Because chat and catalogue shared one column, reachat's horizontal flex layout also stole width from the selector.The fix
App.tsx,index.css): a fixed 320px sidebar (brand, New chat, conversations list, engine picker, model catalogue, export/import) beside an always-visible.chat-maincolumn (status bar + active provider + composer). The catalogue grid is confined to the sidebar and forced to one column.FormalAiProvider): avatars, per-message copy, markdown + code, "Thinking…" indicator, auto-growing composer (Enter to send, Shift+Enter newline).types/chat.tsand switchable from the sidebar.storage/links-notation.ts,storage/conversations.ts): conversations persisted tolocalStorage(mib_conversations_lino) as amodel_in_browser_bundle; Export downloadsmodel-in-browser-chats.lino; Import restores from a.linobundle.min-width/widthbackstops on.reachat-containerand.chat-main.Verification
tsc --noEmitclean;vitest37/37 pass;vite buildsucceeds;scripts/check-file-size.mjspasses.e2e/chat-ux.spec.ts— 6/6 pass: composer reachable beside the catalogue (R1), every engine selectable without narrowing the selector (R3/R7), conversation create/switch/delete, Links Notation persistence across reload, and.linoexport/import round-trips.e2e/inference.spec.ts— 6/6 pass: pins SmolLM2 135M Instruct (q8), auto-loads, sends real messages, and asserts streamed replies (R6).Case study
Full analysis (timeline, root cause, solution plans, library/online research, citations) in
docs/case-studies/issue-15/README.md.Closes #15