Skip to content

Commit 2d0353a

Browse files
authored
improvement(search): polish organization search and source results (#8222)
* improvement(search): polish organization search and source results * fix(search): align activity headers and preserve complete drafts * fix(search): tighten draft recovery and shared sidebar behavior
1 parent 1702bc1 commit 2d0353a

66 files changed

Lines changed: 1523 additions & 665 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/sim/app/layout.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
112112
// needs the same variables set before first paint.
113113
try {
114114
var path = window.location.pathname;
115-
if (path.indexOf('/workspace/') === -1 && path.indexOf('/o/') !== 0) {
115+
if (path.indexOf('/workspace/') === -1 && path.indexOf('/o/') !== 0 && path.indexOf('/account/settings') !== 0 && path.indexOf('/selfhost/settings') !== 0) {
116116
return;
117117
}
118118
} catch (e) {
@@ -123,7 +123,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
123123
// 30% of the viewport capped at 400px, and never below the 224px
124124
// minimum, so a narrow window yields a width >= MIN instead of a
125125
// sub-minimum sliver.
126-
var defaultSidebarWidth = 256;
126+
var defaultSidebarWidth = 224;
127127
try {
128128
// Collapse comes from the cookie (independent of localStorage
129129
// parsing); the persisted width is read defensively below. Match the

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/chats-section/chats-section.tsx‎

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { chipVariants, cn, DropdownMenuItem, Loader, OverflowText, Skeleton } from '@sim/emcn'
3+
import { chipVariants, cn, DropdownMenuItem, OverflowText } from '@sim/emcn'
44
import { MoreHorizontal, Pin, Task } from '@sim/emcn/icons'
55
import type { OrganizationChat } from '@/app/o/[organizationId]/components/organization-sidebar/hooks'
66
import { useOrganizationChatActions } from '@/app/o/[organizationId]/components/organization-sidebar/hooks/use-organization-chat-actions'
@@ -20,15 +20,6 @@ import {
2020
SIDEBAR_SECTION_GAP_CLASS,
2121
} from '@/app/workspace/[workspaceId]/w/components/sidebar/constants'
2222

23-
/** Stands in for a chip row while the list loads, so it carries no margin either. */
24-
function ChatRowSkeleton() {
25-
return (
26-
<div className='sidebar-collapse-hide flex h-[30px] items-center gap-2 rounded-lg px-2'>
27-
<Skeleton className='size-[16px] shrink-0 rounded-sm' />
28-
</div>
29-
)
30-
}
31-
3223
interface ChatRowProps {
3324
chat: OrganizationChat
3425
isCurrentRoute: boolean
@@ -132,12 +123,7 @@ export function ChatsSection({
132123
ariaLabel='Chats'
133124
isEditing={rename.editingId !== null}
134125
>
135-
{isLoading ? (
136-
<DropdownMenuItem disabled>
137-
<Loader className='size-[14px]' animate />
138-
Loading...
139-
</DropdownMenuItem>
140-
) : chats.length === 0 ? (
126+
{isLoading ? null : chats.length === 0 ? (
141127
<DropdownMenuItem disabled>No chats yet</DropdownMenuItem>
142128
) : (
143129
chats.map((chat) => (
@@ -163,9 +149,7 @@ export function ChatsSection({
163149
</div>
164150
) : (
165151
<div className={cn(SIDEBAR_ITEM_GAP_CLASS, 'flex flex-col px-2')}>
166-
{isLoading ? (
167-
<ChatRowSkeleton />
168-
) : (
152+
{!isLoading && (
169153
<>
170154
{chats.length === 0 && (
171155
<div className='flex h-[30px] items-center px-2 text-[var(--text-muted)] text-small'>

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/workspaces-section/workspace-list.test.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ describe('WorkspaceList rail view', () => {
146146
expect(document.querySelector('input')).toBeNull()
147147
})
148148

149-
it('shows the loading row while the list resolves', async () => {
149+
it('keeps the list quiet while it resolves', async () => {
150150
workspacesState.isLoading = true
151151
await render()
152-
expect(document.body.textContent).toContain('Loading...')
152+
expect(document.body.textContent).not.toContain('Loading...')
153153
})
154154
})

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/workspaces-section/workspace-list.tsx‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
cn,
77
DropdownMenuItem,
88
DropdownMenuItemAction,
9-
Loader,
109
OverflowText,
1110
toast,
1211
} from '@sim/emcn'
@@ -76,12 +75,6 @@ export function WorkspaceList({ organizationId, pathname, flyout }: WorkspaceLis
7675

7776
return (
7877
<>
79-
{isLoading && flyout && (
80-
<DropdownMenuItem disabled>
81-
<Loader className='size-[14px]' animate />
82-
Loading...
83-
</DropdownMenuItem>
84-
)}
8578
{!isLoading && workspaces.length === 0 && (
8679
<div className='px-2 py-1 text-[var(--text-muted)] text-small'>No workspaces yet</div>
8780
)}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export { useCollapsedTooltips } from './use-collapsed-tooltips'
21
export type { OrganizationChat } from './use-organization-chats'
32
export { useOrganizationChats } from './use-organization-chats'
43
export { useOrganizationWorkspaces } from './use-organization-workspaces'

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/hooks/use-collapsed-tooltips.ts‎

Lines changed: 0 additions & 23 deletions
This file was deleted.

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/organization-sidebar.tsx‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ import {
1616
OrganizationHeader,
1717
WorkspacesSection,
1818
} from '@/app/o/[organizationId]/components/organization-sidebar/components'
19-
import {
20-
useCollapsedTooltips,
21-
useOrganizationChats,
22-
} from '@/app/o/[organizationId]/components/organization-sidebar/hooks'
19+
import { useOrganizationChats } from '@/app/o/[organizationId]/components/organization-sidebar/hooks'
2320
import { buildOrganizationNavItems } from '@/app/o/[organizationId]/components/organization-sidebar/navigation'
2421
import { useOrganizationContext } from '@/app/o/[organizationId]/providers/organization-provider'
2522
import { OrganizationSettingsSidebar } from '@/app/o/[organizationId]/settings/organization-settings-sidebar'
@@ -85,7 +82,7 @@ export const OrganizationSidebar = memo(function OrganizationSidebar() {
8582
useOrganizationContext()
8683
const toggleCollapsed = useSidebarStore((state) => state.toggleCollapsed)
8784
const { handlePointerDown } = useSidebarResize()
88-
const showCollapsedTooltips = useCollapsedTooltips(isCollapsed)
85+
const showCollapsedTooltips = isCollapsed
8986
const scrollEdges = useScrollEdges(scrollContainerRef, {
9087
contentRef: scrollContentRef,
9188
enabled: !isCollapsed,

‎apps/sim/app/o/[organizationId]/components/search-input-bar.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ export function SearchInputBar({
8181
<div
8282
ref={scrollerRef}
8383
className={cn(
84-
'relative min-h-[46px] w-full rounded-[23px] border border-[var(--border-1)] bg-[var(--white)] py-[7px] pr-2.5 pl-4 dark:bg-[var(--surface-4)]',
85-
hasAttachments && 'rounded-2xl px-2.5 py-2',
84+
'relative min-h-[46px] w-full rounded-full border border-[var(--border-1)] bg-[var(--white)] px-2.5 py-2 dark:bg-[var(--surface-4)]',
85+
(hasAttachments || expanded) && 'rounded-2xl',
8686
floating && 'shadow-ambient'
8787
)}
8888
>

‎apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -617,15 +617,15 @@ it('shows Build with a chevron in the shared chip and text-only modes in its men
617617
onModeChange,
618618
})
619619
const mode = container.querySelector<HTMLButtonElement>('[aria-label="Conversation mode"]')!
620-
expect(mode.parentElement?.previousElementSibling).toBeNull()
621-
expect(mode.parentElement?.nextElementSibling?.getAttribute('aria-label')).toBe('Add resources')
620+
expect(mode.parentElement?.previousElementSibling?.getAttribute('aria-label')).toBe('Skills')
621+
expect(mode.parentElement?.nextElementSibling).toBeNull()
622622
expect(mode.textContent).toBe('Build')
623623
expect(mode.querySelectorAll('svg')).toHaveLength(1)
624624
await act(async () =>
625625
mode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
626626
)
627627
const search = [...document.querySelectorAll<HTMLElement>('[role="menuitem"]')].find(
628-
(item) => item.textContent === 'Search'
628+
(item) => item.textContent === 'Ask'
629629
)!
630630
expect(search.querySelector('svg')).toBeNull()
631631
expect(document.querySelector('[role="tooltip"]')).toBeNull()
@@ -729,12 +729,12 @@ it.each(['skill', 'file'] as const)(
729729
mode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
730730
)
731731
const search = [...document.querySelectorAll<HTMLElement>('[role="menuitem"]')].find(
732-
(item) => item.textContent === 'Search'
732+
(item) => item.textContent === 'Ask'
733733
)!
734734
await act(async () => search.click())
735735
expect(onModeChange).not.toHaveBeenCalled()
736736
expect(info).toHaveBeenCalledWith(
737-
'Remove resource and skill mentions and non-image attachments before switching to Search.'
737+
'Remove resource and skill mentions and non-image attachments before switching to Ask.'
738738
)
739739
expect(
740740
container.querySelector<HTMLInputElement | HTMLTextAreaElement>('[aria-label="Ask Sim"]')!
@@ -786,8 +786,8 @@ describe('Search levels', () => {
786786
expect(
787787
row.querySelector<HTMLInputElement | HTMLTextAreaElement>('[aria-label="Ask Sim"]')
788788
).not.toBeNull()
789-
expect(mode.textContent).toBe('')
790-
expect(mode.querySelectorAll('svg')).toHaveLength(2)
789+
expect(mode.textContent).toBe('Ask')
790+
expect(mode.querySelectorAll('svg')).toHaveLength(1)
791791
expect(mode.parentElement?.nextElementSibling).toBeNull()
792792
expect(row.querySelector('[aria-label="Search level"]')).toBeNull()
793793
})

‎apps/sim/app/o/[organizationId]/home/components/composer/composer.tsx‎

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ interface ComposerProps {
4343
/** On the empty home the placeholder types itself and the field is taller; in a chat it is the plain footer input. */
4444
isInitialView: boolean
4545
isSending: boolean
46-
onChange: (value: string) => void
46+
onChange: (value: string, contexts?: ChatContext[]) => void
4747
restoredContexts?: ChatContext[]
4848
onSubmit: (text: string, contexts?: ChatContext[]) => void
4949
onStop: () => void
@@ -91,6 +91,7 @@ export function Composer({
9191
organizationId: organization.id,
9292
contextsEnabled: !imagesOnly,
9393
initialValue: value,
94+
initialContexts: restoredContexts,
9495
onPasteFiles: files.processFiles,
9596
})
9697
const { textareaRef } = editor
@@ -105,13 +106,16 @@ export function Composer({
105106
if (value) textareaRef.current?.focus()
106107
}
107108
}, [value, textareaRef])
109+
const lastContexts = useRef(editor.contexts)
108110
useEffect(() => {
109111
if (editorRef.current.getValue() !== editor.value) return
110-
if (editor.value !== lastPublished.current) {
111-
lastPublished.current = editor.value
112-
onChange(editor.value)
112+
const plainValue = editorRef.current.getPlainValue()
113+
if (plainValue !== lastPublished.current || editor.contexts !== lastContexts.current) {
114+
lastPublished.current = plainValue
115+
lastContexts.current = editor.contexts
116+
onChange(plainValue, editor.contexts.length ? editor.contexts : undefined)
113117
}
114-
}, [editor.value, onChange])
118+
}, [editor.value, editor.contexts, onChange])
115119
useEffect(() => {
116120
if (!restoredContexts) return
117121
// A queued skill may belong to a workspace whose picker has never opened here.
@@ -182,6 +186,25 @@ export function Composer({
182186

183187
const leadingControls = (
184188
<>
189+
{imagesOnly && !showModeSelector && (
190+
<Search className='size-[16px] shrink-0 text-[var(--text-icon)]' />
191+
)}
192+
{!imagesOnly && contextPicker('resources', Plus, 'Add resources')}
193+
194+
{!imagesOnly && (
195+
<Tooltip.Root>
196+
<Tooltip.Trigger asChild>
197+
<Chip
198+
shape='round'
199+
leftIcon={Paperclip}
200+
onClick={files.handleFileSelect}
201+
aria-label='Attach file'
202+
/>
203+
</Tooltip.Trigger>
204+
<Tooltip.Content side='top'>Attach file</Tooltip.Content>
205+
</Tooltip.Root>
206+
)}
207+
{!imagesOnly && contextPicker('skills', Slash, 'Skills')}
185208
{showModeSelector && (
186209
<ConversationModeSelector
187210
value={requestMode}
@@ -195,7 +218,7 @@ export function Composer({
195218
files.attachedFiles.some((file) => !isAssistantImageType(file.type)))
196219
) {
197220
toast.info(
198-
'Remove resource and skill mentions and non-image attachments before switching to Search.'
221+
'Remove resource and skill mentions and non-image attachments before switching to Ask.'
199222
)
200223
return
201224
}
@@ -205,25 +228,6 @@ export function Composer({
205228
}
206229
/>
207230
)}
208-
{imagesOnly && !showModeSelector && (
209-
<Search className='size-[16px] shrink-0 text-[var(--text-icon)]' />
210-
)}
211-
{!imagesOnly && contextPicker('resources', Plus, 'Add resources')}
212-
213-
{!imagesOnly && (
214-
<Tooltip.Root>
215-
<Tooltip.Trigger asChild>
216-
<Chip
217-
shape='round'
218-
leftIcon={Paperclip}
219-
onClick={files.handleFileSelect}
220-
aria-label='Attach file'
221-
/>
222-
</Tooltip.Trigger>
223-
<Tooltip.Content side='top'>Attach file</Tooltip.Content>
224-
</Tooltip.Root>
225-
)}
226-
{!imagesOnly && contextPicker('skills', Slash, 'Skills')}
227231
</>
228232
)
229233
const voiceControl = voice.isSupported && (

0 commit comments

Comments
 (0)