Skip to content

Commit 1181c7a

Browse files
committed
Release v0.0.18
## What's New in v0.0.18 ### Improvements & Fixes - Fixed sidebar flickering when switching between chats
1 parent a8c54fa commit 1181c7a

10 files changed

Lines changed: 83 additions & 43 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "21st-desktop",
3-
"version": "0.0.17",
3+
"version": "0.0.18",
44
"private": true,
55
"description": "1Code - UI for parallel work with AI agents",
66
"author": "21st.dev",

src/renderer/components/chat-markdown-renderer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,8 @@ export const ChatMarkdownRenderer = memo(function ChatMarkdownRenderer({
442442
"[&_li>p]:inline [&_li>p]:mb-0",
443443
// Prevent horizontal overflow on mobile
444444
"overflow-hidden break-words",
445+
// Global spacing: elements before hr get extra bottom margin (for spacing above divider)
446+
"[&_p:has(+hr)]:mb-6 [&_ul:has(+hr)]:mb-6 [&_ol:has(+hr)]:mb-6 [&_div:has(+hr)]:mb-6 [&_table:has(+hr)]:mb-6 [&_h1:has(+hr)]:mb-6 [&_h2:has(+hr)]:mb-6 [&_h3:has(+hr)]:mb-6 [&_blockquote:has(+hr)]:mb-6",
445447
// Global spacing: elements after hr get extra top margin
446448
"[&_hr+p]:mt-4 [&_hr+ul]:mt-4 [&_hr+ol]:mt-4",
447449
// Global spacing: elements after code blocks get extra top margin

src/renderer/components/ui/kbd.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from "react"
22
import { cn } from "../../lib/utils"
3-
import { CmdIcon, OptionIcon, ShiftIcon } from "./icons"
3+
import { CmdIcon, EnterIcon, OptionIcon, ShiftIcon } from "./icons"
44

55
export interface KbdProps extends React.HTMLAttributes<HTMLElement> {}
66

@@ -16,10 +16,11 @@ function renderShortcut(children: React.ReactNode): React.ReactNode {
1616
"⌥": <OptionIcon key="opt" className="h-3 w-3" />,
1717
"⇧": <ShiftIcon key="shift" className="h-3 w-3" />,
1818
"⌃": <span key="ctrl"></span>, // Control stays as unicode (no icon)
19+
"↵": <EnterIcon key="enter" className="h-3 w-3" />,
1920
}
2021

2122
// Split by symbols and replace with icons
22-
const regex = /([])/g
23+
const regex = /([])/g
2324
const tokens = children.split(regex)
2425

2526
tokens.forEach((token, index) => {

src/renderer/features/agents/main/active-chat.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,25 @@ function ChatViewInner({
22202220
})
22212221
}, [hasUnapprovedPlan, subChatId, setPendingPlanApprovals])
22222222

2223+
// Keyboard shortcut: Cmd+Enter to approve plan
2224+
useEffect(() => {
2225+
const handleKeyDown = (e: KeyboardEvent) => {
2226+
if (
2227+
e.key === "Enter" &&
2228+
e.metaKey &&
2229+
!e.shiftKey &&
2230+
hasUnapprovedPlan &&
2231+
!isStreaming
2232+
) {
2233+
e.preventDefault()
2234+
handleApprovePlan()
2235+
}
2236+
}
2237+
2238+
window.addEventListener("keydown", handleKeyDown)
2239+
return () => window.removeEventListener("keydown", handleKeyDown)
2240+
}, [hasUnapprovedPlan, isStreaming, handleApprovePlan])
2241+
22232242
// Clean up pending plan approval when unmounting
22242243
useEffect(() => {
22252244
return () => {
@@ -2999,7 +3018,7 @@ function ChatViewInner({
29993018
{/* Input */}
30003019
<div
30013020
className={cn(
3002-
"px-2 pb-6 shadow-sm shadow-background relative z-10",
3021+
"px-2 pb-2 shadow-sm shadow-background relative z-10",
30033022
(isStreaming || changedFilesForSubChat.length > 0) &&
30043023
!(pendingQuestions?.subChatId === subChatId) &&
30053024
"-mt-3 pt-3",
@@ -3022,7 +3041,7 @@ function ChatViewInner({
30223041
isDragOver && "ring-2 ring-primary/50 border-primary/50",
30233042
isFocused && !isDragOver && "ring-2 ring-primary/50",
30243043
)}
3025-
maxHeight={200}
3044+
maxHeight={240}
30263045
onSubmit={handleSend}
30273046
contextItems={
30283047
images.length > 0 || files.length > 0 ? (
@@ -3091,7 +3110,7 @@ function ChatViewInner({
30913110
onShiftTab={() => setIsPlanMode((prev) => !prev)}
30923111
placeholder="Plan, @ for context, / for commands"
30933112
className={cn(
3094-
"bg-transparent max-h-[200px] overflow-y-auto p-1",
3113+
"bg-transparent max-h-[240px] overflow-y-auto p-1",
30953114
isMobile && "min-h-[56px]",
30963115
)}
30973116
onPaste={handlePaste}
@@ -3360,8 +3379,10 @@ function ChatViewInner({
33603379
size="sm"
33613380
className="h-7 gap-1.5 rounded-lg"
33623381
>
3363-
<CheckIcon className="w-3.5 h-3.5" />
33643382
Implement plan
3383+
<Kbd className="text-primary-foreground/80">
3384+
⌘↵
3385+
</Kbd>
33653386
</Button>
33663387
) : (
33673388
<AgentSendButton

src/renderer/features/agents/main/new-chat-form.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,7 @@ export function NewChatForm({
942942
</div>
943943
</div>
944944

945-
<div className="flex flex-1 items-center justify-center overflow-y-auto relative pb-16">
945+
<div className="flex flex-1 items-center justify-center overflow-y-auto relative">
946946
<div className="w-full max-w-2xl space-y-4 md:space-y-6 relative z-10 px-4">
947947
{/* Title - only show when project is selected */}
948948
{validatedProject && (
@@ -983,7 +983,7 @@ export function NewChatForm({
983983
isDragOver && "ring-2 ring-primary/50 border-primary/50",
984984
isFocused && !isDragOver && "ring-2 ring-primary/50",
985985
)}
986-
maxHeight={200}
986+
maxHeight={240}
987987
onSubmit={handleSend}
988988
contextItems={contextItems}
989989
>
@@ -1000,7 +1000,7 @@ export function NewChatForm({
10001000
onShiftTab={() => setIsPlanMode((prev) => !prev)}
10011001
placeholder="Plan, @ for context, / for commands"
10021002
className={cn(
1003-
"bg-transparent max-h-[200px] overflow-y-auto p-1",
1003+
"bg-transparent max-h-[240px] overflow-y-auto p-1",
10041004
isMobileFullscreen ? "min-h-[56px]" : "min-h-[44px]",
10051005
)}
10061006
onPaste={handlePaste}

src/renderer/features/agents/ui/agents-content.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
useAgentSubChatStore,
4141
type SubChatMeta,
4242
} from "../stores/sub-chat-store"
43+
import { useShallow } from "zustand/react/shallow"
4344
import { motion, AnimatePresence } from "motion/react"
4445
// import { ResizableSidebar } from "@/app/(alpha)/canvas/[id]/{components}/resizable-sidebar"
4546
import { ResizableSidebar } from "../../../components/ui/resizable-sidebar"
@@ -119,12 +120,14 @@ export function AgentsContent() {
119120
subChatQuickSwitchOpenRef.current = subChatQuickSwitchOpen
120121
subChatQuickSwitchSelectedIndexRef.current = subChatQuickSwitchSelectedIndex
121122

122-
// Get sub-chats from store
123-
const allSubChats = useAgentSubChatStore((state) => state.allSubChats)
124-
const openSubChatIds = useAgentSubChatStore((state) => state.openSubChatIds)
125-
const activeSubChatId = useAgentSubChatStore((state) => state.activeSubChatId)
126-
const setActiveSubChat = useAgentSubChatStore(
127-
(state) => state.setActiveSubChat,
123+
// Get sub-chats from store with shallow comparison
124+
const { allSubChats, openSubChatIds, activeSubChatId, setActiveSubChat } = useAgentSubChatStore(
125+
useShallow((state) => ({
126+
allSubChats: state.allSubChats,
127+
openSubChatIds: state.openSubChatIds,
128+
activeSubChatId: state.activeSubChatId,
129+
setActiveSubChat: state.setActiveSubChat,
130+
}))
128131
)
129132

130133
// Fetch teams for header

src/renderer/features/agents/ui/archive-popover.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,24 @@ export function ArchivePopover({ trigger }: ArchivePopoverProps) {
9292
)
9393
}, [archivedChats, searchQuery])
9494

95-
// Sync selected index with selected chat when popover opens or data changes
95+
// Clear search query and sync selected index when popover opens
96+
useEffect(() => {
97+
if (open) {
98+
setSearchQuery("")
99+
setTimeout(() => {
100+
searchInputRef.current?.focus()
101+
}, 0)
102+
}
103+
}, [open, setSearchQuery])
104+
105+
// Sync selected index with filtered chats
96106
useEffect(() => {
97107
if (open && filteredChats.length > 0) {
98108
// Find index of currently selected chat, default to 0 if not found
99109
const currentIndex = filteredChats.findIndex(
100110
(chat) => chat.id === selectedChatId,
101111
)
102112
setSelectedIndex(currentIndex >= 0 ? currentIndex : 0)
103-
setTimeout(() => {
104-
searchInputRef.current?.focus()
105-
}, 0)
106113
}
107114
}, [open, filteredChats, selectedChatId])
108115

@@ -123,6 +130,8 @@ export function ArchivePopover({ trigger }: ArchivePopoverProps) {
123130
const chat = filteredChats[selectedIndex]
124131
if (chat) {
125132
restoreMutation.mutate({ id: chat.id })
133+
setSelectedChatId(chat.id)
134+
setOpen(false)
126135
}
127136
}
128137
}

src/renderer/features/agents/ui/sub-chat-selector.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
useAgentSubChatStore,
2727
type SubChatMeta,
2828
} from "../stores/sub-chat-store"
29+
import { useShallow } from "zustand/react/shallow"
2930
import { PopoverTrigger } from "../../../components/ui/popover"
3031
import {
3132
Tooltip,
@@ -76,15 +77,16 @@ export function SubChatSelector({
7677
isDiffSidebarOpen = false,
7778
diffStats,
7879
}: SubChatSelectorProps) {
79-
const activeSubChatId = useAgentSubChatStore((state) => state.activeSubChatId)
80-
const openSubChatIds = useAgentSubChatStore((state) => state.openSubChatIds)
81-
const pinnedSubChatIds = useAgentSubChatStore(
82-
(state) => state.pinnedSubChatIds,
83-
)
84-
const allSubChats = useAgentSubChatStore((state) => state.allSubChats)
85-
const parentChatId = useAgentSubChatStore((state) => state.chatId)
86-
const togglePinSubChat = useAgentSubChatStore(
87-
(state) => state.togglePinSubChat,
80+
// Use shallow comparison to prevent re-renders when arrays have same content
81+
const { activeSubChatId, openSubChatIds, pinnedSubChatIds, allSubChats, parentChatId, togglePinSubChat } = useAgentSubChatStore(
82+
useShallow((state) => ({
83+
activeSubChatId: state.activeSubChatId,
84+
openSubChatIds: state.openSubChatIds,
85+
pinnedSubChatIds: state.pinnedSubChatIds,
86+
allSubChats: state.allSubChats,
87+
parentChatId: state.chatId,
88+
togglePinSubChat: state.togglePinSubChat,
89+
}))
8890
)
8991
const [loadingSubChats] = useAtom(loadingSubChatsAtom)
9092
const subChatUnseenChanges = useAtomValue(agentsSubChatUnseenChangesAtom)
@@ -97,7 +99,7 @@ export function SubChatSelector({
9799
// Pending plan approvals from DB - only for open sub-chats
98100
const { data: pendingPlanApprovalsData } = trpc.chats.getPendingPlanApprovals.useQuery(
99101
{ openSubChatIds },
100-
{ refetchInterval: 5000, enabled: openSubChatIds.length > 0 }
102+
{ refetchInterval: 5000, enabled: openSubChatIds.length > 0, placeholderData: (prev) => prev }
101103
)
102104
const pendingPlanApprovals = useMemo(() => {
103105
const set = new Set<string>()

src/renderer/features/sidebar/agents-sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,13 @@ export function AgentsSidebar({
365365
// File changes stats from DB - only for open sub-chats
366366
const { data: fileStatsData } = trpc.chats.getFileStats.useQuery(
367367
{ openSubChatIds: allOpenSubChatIds },
368-
{ refetchInterval: 5000, enabled: allOpenSubChatIds.length > 0 }
368+
{ refetchInterval: 5000, enabled: allOpenSubChatIds.length > 0, placeholderData: (prev) => prev }
369369
)
370370

371371
// Pending plan approvals from DB - only for open sub-chats
372372
const { data: pendingPlanApprovalsData } = trpc.chats.getPendingPlanApprovals.useQuery(
373373
{ openSubChatIds: allOpenSubChatIds },
374-
{ refetchInterval: 5000, enabled: allOpenSubChatIds.length > 0 }
374+
{ refetchInterval: 5000, enabled: allOpenSubChatIds.length > 0, placeholderData: (prev) => prev }
375375
)
376376

377377
// Fetch all projects for git info

src/renderer/features/sidebar/agents-subchats-sidebar.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
useAgentSubChatStore,
3535
type SubChatMeta,
3636
} from "../agents/stores/sub-chat-store"
37+
import { useShallow } from "zustand/react/shallow"
3738
import {
3839
PlusIcon,
3940
ArchiveIcon,
@@ -103,15 +104,16 @@ export function AgentsSubChatsSidebar({
103104
isLoading = false,
104105
agentName,
105106
}: AgentsSubChatsSidebarProps) {
106-
const activeSubChatId = useAgentSubChatStore((state) => state.activeSubChatId)
107-
const openSubChatIds = useAgentSubChatStore((state) => state.openSubChatIds)
108-
const pinnedSubChatIds = useAgentSubChatStore(
109-
(state) => state.pinnedSubChatIds,
110-
)
111-
const allSubChats = useAgentSubChatStore((state) => state.allSubChats)
112-
const parentChatId = useAgentSubChatStore((state) => state.chatId)
113-
const togglePinSubChat = useAgentSubChatStore(
114-
(state) => state.togglePinSubChat,
107+
// Use shallow comparison to prevent re-renders when arrays have same content
108+
const { activeSubChatId, openSubChatIds, pinnedSubChatIds, allSubChats, parentChatId, togglePinSubChat } = useAgentSubChatStore(
109+
useShallow((state) => ({
110+
activeSubChatId: state.activeSubChatId,
111+
openSubChatIds: state.openSubChatIds,
112+
pinnedSubChatIds: state.pinnedSubChatIds,
113+
allSubChats: state.allSubChats,
114+
parentChatId: state.chatId,
115+
togglePinSubChat: state.togglePinSubChat,
116+
}))
115117
)
116118
const [loadingSubChats] = useAtom(loadingSubChatsAtom)
117119
const subChatFiles = useAtomValue(subChatFilesAtom)
@@ -154,7 +156,7 @@ export function AgentsSubChatsSidebar({
154156
// Pending plan approvals from DB - only for open sub-chats
155157
const { data: pendingPlanApprovalsData } = trpc.chats.getPendingPlanApprovals.useQuery(
156158
{ openSubChatIds },
157-
{ refetchInterval: 5000, enabled: openSubChatIds.length > 0 }
159+
{ refetchInterval: 5000, enabled: openSubChatIds.length > 0, placeholderData: (prev) => prev }
158160
)
159161
const pendingPlanApprovals = useMemo(() => {
160162
const set = new Set<string>()
@@ -1092,7 +1094,7 @@ export function AgentsSubChatsSidebar({
10921094
{/* Loading state - centered spinner */}
10931095
{isLoading ? (
10941096
<div className="flex items-center justify-center h-full">
1095-
<IconSpinner className="w-5 h-5 text-muted-foreground" />
1097+
<IconSpinner className="w-4 h-4 text-muted-foreground" />
10961098
</div>
10971099
) : (
10981100
<>

0 commit comments

Comments
 (0)