Skip to content

Commit a3b19fb

Browse files
committed
improvement(user-input): ui, files
1 parent 21404d1 commit a3b19fb

5 files changed

Lines changed: 108 additions & 79 deletions

File tree

apps/sim/app/api/copilot/chat/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
} from '@/lib/copilot/chat-streaming'
1515
import { COPILOT_REQUEST_MODES } from '@/lib/copilot/models'
1616
import { orchestrateCopilotStream } from '@/lib/copilot/orchestrator'
17-
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
1817
import {
1918
authenticateCopilotRequestSessionOnly,
2019
createBadRequestResponse,
@@ -23,6 +22,7 @@ import {
2322
createUnauthorizedResponse,
2423
} from '@/lib/copilot/request-helpers'
2524
import { resolveWorkflowIdForUser } from '@/lib/workflows/utils'
25+
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
2626

2727
const logger = createLogger('CopilotChatAPI')
2828

apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
'use client'
22

3-
import { Check, CircleAlert, Loader2 } from 'lucide-react'
43
import ReactMarkdown from 'react-markdown'
54
import remarkGfm from 'remark-gfm'
65
import { cn } from '@/lib/core/utils/cn'
76
import type { ContentBlock, ToolCallStatus } from '../../types'
87

98
const REMARK_PLUGINS = [remarkGfm]
10-
const ICON_BASE = 'h-[12px] w-[12px] flex-shrink-0'
11-
12-
function StatusIcon({ status }: { status: ToolCallStatus }) {
13-
switch (status) {
14-
case 'executing':
15-
return <Loader2 className={cn(ICON_BASE, 'animate-spin text-[var(--text-tertiary)]')} />
16-
case 'success':
17-
return <Check className={cn(ICON_BASE, 'text-emerald-500')} />
18-
case 'error':
19-
return <CircleAlert className={cn(ICON_BASE, 'text-red-400')} />
20-
}
21-
}
9+
10+
const PROSE_CLASSES = cn(
11+
'prose prose-base dark:prose-invert max-w-none font-body font-[380]',
12+
'prose-headings:font-semibold prose-headings:tracking-[-0.01em] prose-headings:text-[var(--text-primary)]',
13+
'prose-headings:mb-[12px] prose-headings:mt-[20px]',
14+
'prose-p:text-[16px] prose-p:leading-[1.75] prose-p:tracking-[-0.015em] prose-p:text-[var(--text-primary)]',
15+
'prose-p:mb-[8px]',
16+
'prose-li:text-[16px] prose-li:leading-[1.75] prose-li:tracking-[-0.015em] prose-li:text-[var(--text-primary)]',
17+
'prose-li:my-[4px]',
18+
'prose-ul:my-[12px] prose-ol:my-[12px]',
19+
'prose-strong:font-semibold prose-strong:text-[var(--text-primary)]',
20+
'prose-a:text-[var(--brand-secondary)]',
21+
'prose-code:rounded-[4px] prose-code:bg-[var(--surface-5)] prose-code:px-[5px] prose-code:py-[2px] prose-code:text-[13px] prose-code:font-mono prose-code:font-normal prose-code:text-[var(--text-primary)]',
22+
'prose-pre:my-[14px] prose-pre:rounded-[8px] prose-pre:bg-[var(--surface-5)] prose-pre:text-[13px]',
23+
'prose-hr:border-[var(--divider)]'
24+
)
2225

2326
function formatToolName(name: string): string {
2427
return name
@@ -112,26 +115,22 @@ export function MessageContent({ blocks, fallbackContent, isStreaming }: Message
112115
if (segments.length === 0) return null
113116

114117
return (
115-
<>
118+
<div className='space-y-[10px]'>
116119
{segments.map((segment, i) => {
117120
if (segment.type === 'text') {
118121
return (
119-
<div
120-
key={`text-${i}`}
121-
className='prose prose-neutral prose-sm dark:prose-invert max-w-none'
122-
>
122+
<div key={`text-${i}`} className={PROSE_CLASSES}>
123123
<ReactMarkdown remarkPlugins={REMARK_PLUGINS}>{segment.content}</ReactMarkdown>
124124
</div>
125125
)
126126
}
127127

128128
return (
129-
<div key={segment.id} className='flex items-center gap-[8px] py-[4px]'>
130-
<StatusIcon status={segment.status} />
131-
<span className='text-[13px] text-[var(--text-secondary)]'>{segment.label}</span>
129+
<div key={segment.id} className='font-base text-[13px] text-[var(--text-tertiary)]'>
130+
{segment.label}
132131
</div>
133132
)
134133
})}
135-
</>
134+
</div>
136135
)
137136
}

apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
'use client'
22

33
import { useCallback, useEffect, useRef, useState } from 'react'
4-
import { ArrowUp, Mic } from 'lucide-react'
4+
import { ArrowUp, Mic, Paperclip } from 'lucide-react'
55
import { Button } from '@/components/emcn'
66
import { cn } from '@/lib/core/utils/cn'
77
import { useAnimatedPlaceholder } from '../../hooks'
88

9-
const TEXTAREA_CLASSES =
10-
'm-0 box-border h-auto max-h-[30vh] min-h-[24px] w-full resize-none overflow-y-auto overflow-x-hidden break-words border-0 bg-transparent px-[4px] py-[4px] font-medium font-sans text-[14px] text-[var(--text-primary)] leading-[20px] outline-none placeholder:text-[var(--text-muted)] focus-visible:ring-0 focus-visible:ring-offset-0 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'
9+
const TEXTAREA_CLASSES = cn(
10+
'm-0 box-border h-auto max-h-[30vh] min-h-[24px] w-full resize-none',
11+
'overflow-y-auto overflow-x-hidden break-words border-0 bg-transparent',
12+
'px-[4px] py-[4px] font-body text-[15px] leading-[24px] tracking-[-0.015em]',
13+
'text-[var(--text-primary)] outline-none',
14+
'placeholder:font-[350] placeholder:text-[var(--text-subtle)]',
15+
'focus-visible:ring-0 focus-visible:ring-offset-0',
16+
'[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'
17+
)
1118

1219
const SEND_BUTTON_BASE = 'h-[28px] w-[28px] rounded-full border-0 p-0 transition-colors'
1320
const SEND_BUTTON_ACTIVE =
@@ -26,7 +33,7 @@ interface UserInputProps {
2633
onSubmit: () => void
2734
isSending: boolean
2835
onStopGeneration: () => void
29-
animate?: boolean
36+
isInitialView?: boolean
3037
}
3138

3239
export function UserInput({
@@ -35,10 +42,10 @@ export function UserInput({
3542
onSubmit,
3643
isSending,
3744
onStopGeneration,
38-
animate = true,
45+
isInitialView = true,
3946
}: UserInputProps) {
4047
const animatedPlaceholder = useAnimatedPlaceholder()
41-
const placeholder = animate ? animatedPlaceholder : 'Send message to Sim'
48+
const placeholder = isInitialView ? animatedPlaceholder : 'Send message to Sim'
4249
const canSubmit = value.trim().length > 0 && !isSending
4350

4451
const [isListening, setIsListening] = useState(false)
@@ -124,7 +131,10 @@ export function UserInput({
124131
return (
125132
<div
126133
onClick={handleContainerClick}
127-
className='mx-auto w-full max-w-[640px] cursor-text rounded-[12px] border border-[var(--border-1)] bg-white px-[10px] py-[8px] shadow-sm dark:bg-[var(--surface-4)]'
134+
className={cn(
135+
'mx-auto w-full max-w-[640px] cursor-text rounded-[20px] border border-[var(--border-1)] bg-[var(--white)] px-[10px] py-[8px] dark:bg-[var(--surface-4)]',
136+
isInitialView && 'shadow-sm'
137+
)}
128138
>
129139
<textarea
130140
ref={textareaRef}
@@ -136,46 +146,54 @@ export function UserInput({
136146
rows={1}
137147
className={TEXTAREA_CLASSES}
138148
/>
139-
<div className='flex items-center justify-end gap-[6px]'>
140-
<button
141-
type='button'
142-
onClick={toggleListening}
143-
className={cn(
144-
'flex h-[28px] w-[28px] items-center justify-center rounded-full transition-colors',
145-
isListening
146-
? 'bg-red-500 text-white hover:bg-red-600'
147-
: 'text-[var(--text-muted)] hover:text-[var(--text-primary)]'
148-
)}
149-
title={isListening ? 'Stop listening' : 'Voice input'}
150-
>
151-
<Mic className='h-[16px] w-[16px]' strokeWidth={2} />
152-
</button>
153-
{isSending ? (
154-
<Button
155-
onClick={onStopGeneration}
156-
className={cn(SEND_BUTTON_BASE, SEND_BUTTON_ACTIVE)}
157-
title='Stop generation'
149+
<div className='flex items-center justify-between'>
150+
<div className='flex h-[28px] w-[28px] cursor-pointer items-center justify-center rounded-full border border-[#F0F0F0] transition-colors hover:bg-[#F7F7F7] dark:border-[#3d3d3d] dark:hover:bg-[#303030]'>
151+
<Paperclip className='h-[14px] w-[14px] text-[var(--text-muted)]' strokeWidth={2} />
152+
</div>
153+
<div className='flex items-center gap-[6px]'>
154+
<button
155+
type='button'
156+
onClick={toggleListening}
157+
className={cn(
158+
'flex h-[28px] w-[28px] items-center justify-center rounded-full transition-colors',
159+
isListening
160+
? 'bg-red-500 text-white hover:bg-red-600'
161+
: 'text-[var(--text-muted)] hover:text-[var(--text-primary)]'
162+
)}
163+
title={isListening ? 'Stop listening' : 'Voice input'}
158164
>
159-
<svg
160-
className='block h-[14px] w-[14px] fill-white dark:fill-black'
161-
viewBox='0 0 24 24'
162-
xmlns='http://www.w3.org/2000/svg'
165+
<Mic className='h-[16px] w-[16px]' strokeWidth={2} />
166+
</button>
167+
{isSending ? (
168+
<Button
169+
onClick={onStopGeneration}
170+
className={cn(SEND_BUTTON_BASE, SEND_BUTTON_ACTIVE)}
171+
title='Stop generation'
163172
>
164-
<rect x='4' y='4' width='16' height='16' rx='3' ry='3' />
165-
</svg>
166-
</Button>
167-
) : (
168-
<Button
169-
onClick={onSubmit}
170-
disabled={!canSubmit}
171-
className={cn(SEND_BUTTON_BASE, canSubmit ? SEND_BUTTON_ACTIVE : SEND_BUTTON_DISABLED)}
172-
>
173-
<ArrowUp
174-
className='block h-[16px] w-[16px] text-white dark:text-black'
175-
strokeWidth={2.25}
176-
/>
177-
</Button>
178-
)}
173+
<svg
174+
className='block h-[14px] w-[14px] fill-white dark:fill-black'
175+
viewBox='0 0 24 24'
176+
xmlns='http://www.w3.org/2000/svg'
177+
>
178+
<rect x='4' y='4' width='16' height='16' rx='3' ry='3' />
179+
</svg>
180+
</Button>
181+
) : (
182+
<Button
183+
onClick={onSubmit}
184+
disabled={!canSubmit}
185+
className={cn(
186+
SEND_BUTTON_BASE,
187+
canSubmit ? SEND_BUTTON_ACTIVE : SEND_BUTTON_DISABLED
188+
)}
189+
>
190+
<ArrowUp
191+
className='block h-[16px] w-[16px] text-white dark:text-black'
192+
strokeWidth={2.25}
193+
/>
194+
</Button>
195+
)}
196+
</div>
179197
</div>
180198
</div>
181199
)

apps/sim/app/workspace/[workspaceId]/home/home.tsx

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

33
import { useCallback, useState } from 'react'
4-
import { Loader2 } from 'lucide-react'
54
import { useParams } from 'next/navigation'
65
import { MessageContent, UserInput } from './components'
76
import { useChat } from './hooks'
@@ -53,8 +52,10 @@ export function Home({ chatId }: HomeProps = {}) {
5352
if (msg.role === 'user') {
5453
return (
5554
<div key={msg.id} className='flex justify-end'>
56-
<div className='max-w-[80%] rounded-[12px] bg-[var(--surface-5)] px-[10px] py-[8px] text-[14px] text-[var(--text-primary)]'>
57-
<p className='whitespace-pre-wrap'>{msg.content}</p>
55+
<div className='max-w-[80%] rounded-[16px] bg-[var(--surface-5)] px-[14px] py-[4px]'>
56+
<p className='whitespace-pre-wrap font-[380] font-body text-[16px] text-[var(--text-primary)] leading-[1.75] tracking-[-0.015em]'>
57+
{msg.content}
58+
</p>
5859
</div>
5960
</div>
6061
)
@@ -65,20 +66,19 @@ export function Home({ chatId }: HomeProps = {}) {
6566

6667
if (!hasBlocks && !msg.content && isThisStreaming) {
6768
return (
68-
<div
69-
key={msg.id}
70-
className='flex items-center gap-[8px] py-[8px] text-[13px] text-[var(--text-tertiary)]'
71-
>
72-
<Loader2 className='h-[14px] w-[14px] animate-spin' />
73-
Thinking...
69+
<div key={msg.id} className='flex items-center gap-[6px] py-[8px]'>
70+
<div className='h-[6px] w-[6px] animate-pulse rounded-full bg-[var(--text-tertiary)]' />
71+
<span className='font-base text-[13px] text-[var(--text-tertiary)]'>
72+
Thinking…
73+
</span>
7474
</div>
7575
)
7676
}
7777

7878
if (!hasBlocks && !msg.content) return null
7979

8080
return (
81-
<div key={msg.id} className='text-[14px] text-[var(--text-primary)]'>
81+
<div key={msg.id}>
8282
<MessageContent
8383
blocks={msg.contentBlocks || []}
8484
fallbackContent={msg.content}
@@ -98,7 +98,7 @@ export function Home({ chatId }: HomeProps = {}) {
9898
onSubmit={handleSubmit}
9999
isSending={isSending}
100100
onStopGeneration={stopGeneration}
101-
animate={false}
101+
isInitialView={false}
102102
/>
103103
</div>
104104
</div>

apps/sim/tailwind.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ export default {
1313
extend: {
1414
fontFamily: {
1515
season: ['var(--font-season)'],
16+
body: [
17+
'ui-sans-serif',
18+
'-apple-system',
19+
'system-ui',
20+
'Segoe UI',
21+
'Helvetica',
22+
'Apple Color Emoji',
23+
'Arial',
24+
'sans-serif',
25+
'Segoe UI Emoji',
26+
'Segoe UI Symbol',
27+
],
1628
mono: ['var(--font-martian-mono)', 'ui-monospace', 'monospace'],
1729
},
1830
fontSize: {

0 commit comments

Comments
 (0)