diff --git a/CHANGELOG.md b/CHANGELOG.md index 76781dd0cd..6a57b8b135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [1.13.7] - 2026-06-28 + +- Chat: with tool calls (such as Bash and Edit) shown expanded by default, scrolling no longer twitches, and slow scrolling no longer jumps past several messages. +- Mobile: in long conversations, older messages now load before you reach the very top, and fast scrolling no longer leaves blank gaps where messages briefly disappear until you scroll back. +- Mobile: the model and agent buttons in the composer are now borderless and cleaner, show the provider logo next to the model name, and shorten long names with an ellipsis; in the model picker the thinking-variant control is plain text with a chevron and each row's controls line up. +- Mobile: interface labels (the model and agent selectors and other small labels) are back to their previous size after 1.13.6 shrank them too much. +- Providers: the Add provider form stays open while provider data refreshes or a model is picked in the background, instead of snapping back to an existing provider. +- CLI: `openchamber update` works again after a missing helper broke the command. + ## [1.13.6] - 2026-06-28 - Chat: scrolling in conversations now stays steady while sending, queueing, streaming, switching sessions, and loading older messages. @@ -11,7 +20,6 @@ All notable changes to this project will be documented in this file. - Context Panel: chat tabs now use the session title and mark the open chat as seen while you are viewing it. - Desktop/macOS: the Dock icon can now show a badge count for chats with unseen activity, with a new Appearance setting to turn it off. - Context Panel: Browser and Preview tabs no longer accumulate duplicate auth tokens in their URLs after reloads or navigation. -- UI: typography classes (ui-header, ui-label, meta, micro) now actually shrink on mobile viewports — they previously rendered at the same size as desktop despite the mobile clamp rules (thanks to @foundryseven). ## [1.13.5] - 2026-06-27 diff --git a/package.json b/package.json index ab6bc430f1..3c10dabec2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openchamber-monorepo", - "version": "1.13.6", + "version": "1.13.7", "description": "OpenChamber monorepo workspace for web, ui, and desktop runtimes", "private": true, "type": "module", diff --git a/packages/electron/package.json b/packages/electron/package.json index 78654969ae..bf44e0139a 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@openchamber/electron", - "version": "1.13.6", + "version": "1.13.7", "private": true, "description": "Electron desktop runtime for OpenChamber", "author": "OpenChamber", diff --git a/packages/ui/package.json b/packages/ui/package.json index f1c5b2196d..156cf183da 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@openchamber/ui", - "version": "1.13.6", + "version": "1.13.7", "private": true, "type": "module", "main": "src/main.tsx", diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index abf4bd4c03..91e972c693 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -4514,7 +4514,7 @@ const ChatInputComponent: React.FC = ({ onOpenSettings, scrollTo {isMobile ? ( <>
-
+
= ({ onOpenSettings, scrollTo />
-
+
handleOpenMobilePanel('model')} className="min-w-0 flex-shrink" /> (); const MESSAGE_LIST_BUFFER_SIZE = 900; +// Touch surfaces fling-scroll natively and dispatch scroll events less often +// than the virtualizer can repaint, so a desktop-sized buffer leaves blank gaps +// during momentum that only fill once measurement catches up. A larger overscan +// keeps more rows mounted around the viewport so fast flings stay populated. +const MOBILE_MESSAGE_LIST_BUFFER_SIZE = 2400; +const resolveMessageListBufferSize = (): number => ( + isMobileSurfaceRuntime() ? MOBILE_MESSAGE_LIST_BUFFER_SIZE : MESSAGE_LIST_BUFFER_SIZE +); const TIMELINE_CACHE_LIMIT = 16; -const estimateHistoryEntryHeight = (entry: RenderEntry | undefined): number => { - if (!entry) { - return 160; - } - - if (entry.kind === 'turn') { - return 180 + Math.min(entry.turn.assistantMessages.length, 4) * 100; - } - - return 140; -}; - const sameKeys = (a: readonly string[] | undefined, b: readonly string[] | undefined): boolean => { if (a === b) return true; if (!a || !b) return false; @@ -982,8 +979,7 @@ const StaticHistoryList = React.memo(({ entries, shouldVirtualize, contentRef, s ref={virtualizerRef} data={entries} cache={virtualCache} - itemSize={virtualCache ? undefined : estimateHistoryEntryHeight(undefined)} - bufferSize={MESSAGE_LIST_BUFFER_SIZE} + bufferSize={resolveMessageListBufferSize()} shift={shift} scrollRef={scrollRef} > diff --git a/packages/ui/src/components/chat/MobileAgentButton.tsx b/packages/ui/src/components/chat/MobileAgentButton.tsx index 1d1db004c1..3a54074539 100644 --- a/packages/ui/src/components/chat/MobileAgentButton.tsx +++ b/packages/ui/src/components/chat/MobileAgentButton.tsx @@ -73,8 +73,8 @@ export const MobileAgentButton: React.FC = ({ onCycleAge onPointerLeave={handlePointerLeave} onContextMenu={(e) => e.preventDefault()} className={cn( - 'inline-flex min-w-0 items-center select-none', - 'rounded-lg border border-border/50 px-1.5', + 'inline-flex min-w-0 items-stretch select-none', + 'rounded-lg', 'typography-micro font-medium', 'focus:outline-none hover:bg-[var(--interactive-hover)]', 'touch-none', @@ -88,7 +88,9 @@ export const MobileAgentButton: React.FC = ({ onCycleAge }} title={agentLabel} > - {agentLabel} + + {agentLabel} + ); }; diff --git a/packages/ui/src/components/chat/MobileModelButton.tsx b/packages/ui/src/components/chat/MobileModelButton.tsx index b34d45ed88..1094183677 100644 --- a/packages/ui/src/components/chat/MobileModelButton.tsx +++ b/packages/ui/src/components/chat/MobileModelButton.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { cn } from '@/lib/utils'; import { useConfigStore } from '@/stores/useConfigStore'; import { getModelDisplayName } from './mobileControlsUtils'; +import { ProviderLogo } from '@/components/ui/ProviderLogo'; import { useI18n } from '@/lib/i18n'; interface MobileModelButtonProps { @@ -12,6 +13,7 @@ interface MobileModelButtonProps { export const MobileModelButton: React.FC = ({ onOpenModel, className }) => { const { t } = useI18n(); const currentModelId = useConfigStore((state) => state.currentModelId); + const currentProviderId = useConfigStore((state) => state.currentProviderId); const getCurrentProvider = useConfigStore((state) => state.getCurrentProvider); const currentProvider = getCurrentProvider(); const modelLabel = getModelDisplayName(currentProvider, currentModelId, t('chat.modelControls.selectModel')); @@ -21,8 +23,8 @@ export const MobileModelButton: React.FC = ({ onOpenMode type="button" onClick={onOpenModel} className={cn( - 'inline-flex min-w-0 items-center justify-center', - 'rounded-lg border border-border/50 px-1.5', + 'inline-flex min-w-0 items-stretch', + 'rounded-lg', 'typography-micro font-medium text-foreground/80', 'focus:outline-none hover:bg-[var(--interactive-hover)]', className @@ -30,8 +32,11 @@ export const MobileModelButton: React.FC = ({ onOpenMode style={{ height: '26px', maxHeight: '26px', minHeight: '26px' }} title={modelLabel} > - - {modelLabel} + + {currentProviderId ? ( + + ) : null} + {modelLabel} ); diff --git a/packages/ui/src/components/chat/ModelControls.tsx b/packages/ui/src/components/chat/ModelControls.tsx index 3e25419528..cca3e5ea07 100644 --- a/packages/ui/src/components/chat/ModelControls.tsx +++ b/packages/ui/src/components/chat/ModelControls.tsx @@ -1671,7 +1671,7 @@ export const ModelControls: React.FC = ({ isSelected && 'bg-interactive-selection/15 text-interactive-selection-foreground' )} > -
+
) : null} -
+
+
+ +
{/* Identity & Role */}
@@ -810,11 +818,12 @@ export const AgentsPage: React.FC = () => {
+ {/* Row 1: Model + Variant */}
{t('settings.agents.page.field.overrideModel')}
-
+
{ }} />
-
- -
-
+
- {t('settings.agents.page.field.variant')} + {t('settings.agents.page.field.variant')} @@ -843,15 +849,12 @@ export const AgentsPage: React.FC = () => {
- {t('settings.agents.page.field.variantHint')} -
-
{shouldUseVariantSelect ? (