improvement: ui

This commit is contained in:
Emir Karabeg
2026-03-03 14:36:38 -08:00
parent 1339915957
commit 82f541e9de
12 changed files with 114 additions and 87 deletions

3
.gitignore vendored
View File

@@ -26,6 +26,9 @@ bun-debug.log*
**/standalone/
sim-standalone.tar.gz
# redis
dump.rdb
# misc
.DS_Store
*.pem

View File

@@ -86,7 +86,7 @@
:root,
.light {
--bg: #fefefe; /* main canvas - neutral near-white */
--surface-1: #fefefe; /* sidebar, panels */
--surface-1: #f9f9f9; /* sidebar, panels */
--surface-2: #ffffff; /* blocks, cards, modals - pure white */
--surface-3: #f7f7f7; /* popovers, headers */
--surface-4: #f5f5f5; /* buttons base */
@@ -95,14 +95,15 @@
--border-1: #e0e0e0; /* stronger border */
--surface-6: #e5e5e5; /* popovers, elevated surfaces */
--surface-7: #d9d9d9;
--surface-active: #eeeeee; /* hover/active state */
--workflow-edge: #e0e0e0; /* workflow handles/edges - matches border-1 */
/* Text - neutral */
--text-primary: #2d2d2d;
--text-secondary: #4e4e4e;
--text-primary: #1a1a1a;
--text-secondary: #525252;
--text-tertiary: #5c5c5c;
--text-muted: #737373;
--text-muted: #707070;
--text-subtle: #8c8c8c;
--text-inverse: #ffffff;
--text-muted-inverse: #a0a0a0;
@@ -212,6 +213,7 @@
--border-1: #3d3d3d;
--surface-6: #454545;
--surface-7: #505050;
--surface-active: #303030; /* hover/active state */
--workflow-edge: #454545; /* workflow handles/edges - same as surface-6 in dark */
@@ -241,7 +243,7 @@
--white: #ffffff;
/* Font weights - standard weights for dark mode */
--font-weight-base: 440;
--font-weight-base: 450;
--font-weight-medium: 480;
--font-weight-semibold: 550;

View File

@@ -113,9 +113,19 @@ export async function POST(req: NextRequest) {
}
if (actualChatId) {
const userMsg = {
id: userMessageId,
role: 'user' as const,
content: message,
timestamp: new Date().toISOString(),
}
await db
.update(copilotChats)
.set({ conversationId: userMessageId })
.set({
messages: [...conversationHistory, userMsg],
conversationId: userMessageId,
})
.where(eq(copilotChats.id, actualChatId))
}

View File

@@ -57,7 +57,7 @@ export function Home({ chatId, streamId }: HomeProps = {}) {
if (!hasMessages) {
return (
<div className='flex h-full flex-col items-center justify-center bg-[#FCFCFC] px-[24px] dark:bg-[var(--surface-1)]'>
<div className='flex h-full flex-col items-center justify-center bg-[#FCFCFC] px-[24px] dark:bg-[var(--surface-2)]'>
<h1 className='mb-[24px] font-[450] font-season text-[32px] text-[var(--text-primary)] tracking-[-0.02em]'>
What do you want to do?
</h1>
@@ -73,7 +73,7 @@ export function Home({ chatId, streamId }: HomeProps = {}) {
}
return (
<div className='flex h-full bg-[#FCFCFC] dark:bg-[var(--surface-1)]'>
<div className='flex h-full bg-[#FCFCFC] dark:bg-[var(--surface-2)]'>
<div className='flex h-full min-w-0 flex-1 flex-col'>
<div className='min-h-0 flex-1 overflow-y-auto px-[16px] py-[16px]'>
<div className='mx-auto max-w-[768px] space-y-[16px]'>

View File

@@ -386,7 +386,7 @@ export function SearchModal({
backgroundClip: 'padding-box',
}}
/>
<span className='truncate font-medium text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
<span className='truncate font-base text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
{workflow.name}
{workflow.isCurrent && ' (current)'}
</span>
@@ -407,7 +407,7 @@ export function SearchModal({
<div className='relative flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center'>
<Blimp className='h-[14px] w-[14px] text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]' />
</div>
<span className='truncate font-medium text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
<span className='truncate font-base text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
{task.name}
</span>
</Command.Item>
@@ -441,7 +441,7 @@ export function SearchModal({
onSelect={() => handleWorkspaceSelect(workspace)}
className='group flex h-[28px] w-full cursor-pointer items-center gap-[8px] rounded-[6px] px-[10px] text-left text-[15px] aria-selected:bg-[var(--border)] aria-selected:shadow-sm data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50'
>
<span className='truncate font-medium text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
<span className='truncate font-base text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
{workspace.name}
{workspace.isCurrent && ' (current)'}
</span>
@@ -481,11 +481,11 @@ export function SearchModal({
<div className='relative flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center'>
<Icon className='h-[14px] w-[14px] text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]' />
</div>
<span className='truncate font-medium text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
<span className='truncate font-base text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
{page.name}
</span>
{page.shortcut && (
<span className='ml-auto flex-shrink-0 font-medium text-[13px] text-[var(--text-subtle)]'>
<span className='ml-auto flex-shrink-0 font-base text-[13px] text-[var(--text-subtle)]'>
{page.shortcut}
</span>
)}
@@ -503,7 +503,7 @@ export function SearchModal({
}
const groupHeadingClassName =
'[&_[cmdk-group-heading]]:pt-[2px] [&_[cmdk-group-heading]]:pb-[4px] [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-[13px] [&_[cmdk-group-heading]]:text-[var(--text-subtle)] [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wide'
'[&_[cmdk-group-heading]]:pt-[2px] [&_[cmdk-group-heading]]:pb-[4px] [&_[cmdk-group-heading]]:font-base [&_[cmdk-group-heading]]:text-[13px] [&_[cmdk-group-heading]]:text-[var(--text-subtle)] [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wide'
interface CommandItemProps {
value: string
@@ -541,7 +541,7 @@ function CommandItem({
)}
/>
</div>
<span className='truncate font-medium text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
<span className='truncate font-base text-[var(--text-tertiary)] group-aria-selected:text-[var(--text-primary)]'>
{children}
</span>
</Command.Item>

View File

@@ -468,7 +468,7 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) {
return (
<div className='flex flex-shrink-0 flex-col border-t px-[13.5px] pt-[8px] pb-[10px]'>
<div className='flex h-[18px] items-center'>
<span className='font-medium text-[12px] text-[var(--text-primary)]'>
<span className='font-base text-[12px] text-[var(--text-primary)]'>
{PLAN_NAMES[planType]}
</span>
</div>
@@ -490,7 +490,7 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) {
<div className='flex min-w-0 flex-1 items-center gap-[6px]'>
{showPlanText && (
<>
<span className='flex-shrink-0 font-medium text-[12px] text-[var(--text-primary)]'>
<span className='flex-shrink-0 font-base text-[12px] text-[var(--text-primary)]'>
{PLAN_NAMES[planType]}
</span>
<div className='h-[14px] w-[1.5px] flex-shrink-0 bg-[var(--divider)]' />
@@ -498,16 +498,16 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) {
)}
<div className='flex min-w-0 flex-1 items-center gap-[4px]'>
{statusText.isError ? (
<span className='font-medium text-[12px] text-[var(--text-error)]'>
<span className='font-base text-[12px] text-[var(--text-error)]'>
{statusText.text}
</span>
) : (
<>
<span className='font-medium text-[12px] text-[var(--text-secondary)] tabular-nums'>
<span className='font-base text-[12px] text-[var(--text-secondary)] tabular-nums'>
${usage.current.toFixed(2)}
</span>
<span className='font-medium text-[12px] text-[var(--text-secondary)]'>/</span>
<span className='font-medium text-[12px] text-[var(--text-secondary)] tabular-nums'>
<span className='font-base text-[12px] text-[var(--text-secondary)]'>/</span>
<span className='font-base text-[12px] text-[var(--text-secondary)] tabular-nums'>
${usage.limit.toFixed(2)}
</span>
</>

View File

@@ -445,9 +445,9 @@ export function FolderItem({
aria-expanded={isExpanded}
aria-label={`${folder.name} folder, ${isExpanded ? 'expanded' : 'collapsed'}`}
className={clsx(
'group mx-[2px] flex h-[28px] cursor-pointer items-center gap-[8px] rounded-[8px] px-[8px] text-[14px]',
!isAnyDragActive && 'hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]',
isSelected ? 'bg-[var(--surface-6)] dark:bg-[var(--surface-5)]' : '',
'group mx-[2px] flex h-[30px] cursor-pointer items-center gap-[8px] rounded-[8px] px-[8px] text-[14px]',
!isAnyDragActive && 'hover:bg-[var(--surface-active)]',
isSelected ? 'bg-[var(--surface-active)]' : '',
(isDragging || (isAnyDragActive && isSelected)) && 'opacity-50'
)}
onClick={handleClick}
@@ -460,19 +460,19 @@ export function FolderItem({
>
<ChevronRight
className={clsx(
'h-[14px] w-[14px] flex-shrink-0 text-[var(--text-secondary)] transition-transform duration-100',
'h-[16px] w-[16px] flex-shrink-0 text-[var(--text-muted)] transition-transform duration-100',
isExpanded && 'rotate-90'
)}
aria-hidden='true'
/>
{isExpanded ? (
<FolderOpen
className='h-[14px] w-[14px] flex-shrink-0 text-[var(--text-secondary)]'
className='h-[16px] w-[16px] flex-shrink-0 text-[var(--text-muted)]'
aria-hidden='true'
/>
) : (
<Folder
className='h-[14px] w-[14px] flex-shrink-0 text-[var(--text-secondary)]'
className='h-[16px] w-[16px] flex-shrink-0 text-[var(--text-muted)]'
aria-hidden='true'
/>
)}
@@ -483,7 +483,7 @@ export function FolderItem({
onChange={(e) => setEditValue(e.target.value)}
onKeyDown={handleRenameKeyDown}
onBlur={handleInputBlur}
className='min-w-0 flex-1 border-0 bg-transparent p-0 font-medium text-[14px] text-[var(--text-primary)] outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0'
className='min-w-0 flex-1 border-0 bg-transparent p-0 font-base text-[14px] text-[var(--text-primary)] outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0'
maxLength={50}
disabled={isRenaming}
onClick={(e) => {
@@ -498,7 +498,7 @@ export function FolderItem({
) : (
<div className='flex min-w-0 flex-1 items-center gap-[8px]'>
<span
className='min-w-0 flex-1 truncate font-medium text-[var(--text-secondary)]'
className='min-w-0 flex-1 truncate font-base text-[var(--text-secondary)]'
onDoubleClick={handleDoubleClick}
>
{folder.name}
@@ -513,7 +513,7 @@ export function FolderItem({
!isAnyDragActive && 'group-hover:opacity-100'
)}
>
<MoreHorizontal className='h-[14px] w-[14px] text-[var(--text-tertiary)]' />
<MoreHorizontal className='h-[16px] w-[16px] text-[var(--text-muted)]' />
</button>
</div>
)}

View File

@@ -360,15 +360,10 @@ export function WorkflowItem({
href={`/workspace/${workspaceId}/w/${workflow.id}`}
data-item-id={workflow.id}
className={clsx(
'group mx-[2px] flex h-[28px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px]',
active && 'bg-[var(--surface-6)] dark:bg-[var(--surface-5)]',
!active &&
!isAnyDragActive &&
'hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]',
isSelected &&
selectedWorkflows.size > 1 &&
!active &&
'bg-[var(--surface-6)] dark:bg-[var(--surface-5)]',
'group mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px]',
active && 'bg-[var(--surface-active)]',
!active && !isAnyDragActive && 'hover:bg-[var(--surface-active)]',
isSelected && selectedWorkflows.size > 1 && !active && 'bg-[var(--surface-active)]',
(isDragging || (isAnyDragActive && isSelected)) && 'opacity-50'
)}
draggable={!isEditing && !dragDisabled}
@@ -378,7 +373,7 @@ export function WorkflowItem({
onContextMenu={handleContextMenu}
>
<div
className='h-[14px] w-[14px] flex-shrink-0 rounded-[4px] border-[2px]'
className='h-[16px] w-[16px] flex-shrink-0 rounded-[4px] border-[2.5px]'
style={{
backgroundColor: workflow.color,
borderColor: `${workflow.color}60`,
@@ -394,7 +389,7 @@ export function WorkflowItem({
onChange={(e) => setEditValue(e.target.value)}
onKeyDown={handleKeyDown}
onBlur={handleInputBlur}
className='w-full min-w-0 border-0 bg-transparent p-0 font-medium text-[14px] text-[var(--text-primary)] outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0'
className='w-full min-w-0 border-0 bg-transparent p-0 font-base text-[14px] text-[var(--text-primary)] outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0'
maxLength={100}
disabled={isRenaming}
onClick={(e) => {
@@ -409,7 +404,7 @@ export function WorkflowItem({
) : (
<div
className={clsx(
'min-w-0 truncate font-medium',
'min-w-0 truncate font-base',
active ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'
)}
onDoubleClick={handleDoubleClick}
@@ -432,7 +427,7 @@ export function WorkflowItem({
!isAnyDragActive && 'group-hover:opacity-100'
)}
>
<MoreHorizontal className='h-[14px] w-[14px] text-[var(--text-tertiary)]' />
<MoreHorizontal className='h-[16px] w-[16px] text-[var(--text-muted)]' />
</button>
</>
)}

View File

@@ -149,6 +149,12 @@ export function WorkspaceHeader({
const activeWorkspaceFull = workspaces.find((w) => w.id === workspaceId) || null
const workspaceInitial = (() => {
const name = activeWorkspace?.name || ''
const stripped = name.replace(/workspace/gi, '').trim()
return (stripped[0] || name[0] || 'W').toUpperCase()
})()
/**
* Opens the context menu for a workspace at the specified position
*/
@@ -310,7 +316,7 @@ export function WorkspaceHeader({
<button
type='button'
aria-label='Switch workspace'
className='group -mx-[6px] flex h-[28px] min-w-0 max-w-full cursor-pointer items-center gap-[8px] rounded-[8px] bg-transparent px-[8px] transition-colors hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]'
className='group flex h-[32px] w-full min-w-0 cursor-pointer items-center gap-[8px] rounded-[8px] border border-[var(--border)] bg-[#FCFCFC] px-[8px] transition-colors hover:bg-[var(--surface-5)] dark:bg-[var(--surface-2)] dark:hover:bg-[var(--surface-3)]'
title={activeWorkspace?.name || 'Loading...'}
onContextMenu={(e) => {
if (activeWorkspaceFull) {
@@ -318,7 +324,10 @@ export function WorkspaceHeader({
}
}}
>
<span className='truncate font-medium text-[14px] text-[var(--text-primary)]'>
<div className='flex h-[20px] w-[20px] flex-shrink-0 items-center justify-center rounded-[4px] bg-[var(--surface-7)] font-medium text-[12px] text-[var(--text-secondary)] leading-none'>
{workspaceInitial}
</div>
<span className='min-w-0 flex-1 truncate text-left font-base text-[14px] text-[var(--text-primary)]'>
{activeWorkspace?.name || 'Loading...'}
</span>
<ChevronDown
@@ -395,7 +404,7 @@ export function WorkspaceHeader({
{workspaces.map((workspace, index) => (
<div key={workspace.id} className={index > 0 ? 'mt-[2px]' : ''}>
{editingWorkspaceId === workspace.id ? (
<div className='flex h-[26px] items-center gap-[8px] rounded-[8px] bg-[var(--surface-5)] px-[6px]'>
<div className='flex h-[26px] items-center gap-[8px] rounded-[8px] bg-[var(--surface-6)] px-[6px]'>
<input
ref={(el) => {
if (el && !hasInputFocusedRef.current) {
@@ -481,11 +490,14 @@ export function WorkspaceHeader({
<button
type='button'
aria-label='Switch workspace'
className='-mx-[6px] flex h-[28px] min-w-0 max-w-full cursor-pointer items-center gap-[8px] rounded-[8px] bg-transparent px-[8px] transition-colors hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]'
className='flex h-[32px] w-full min-w-0 items-center gap-[8px] rounded-[8px] border border-[var(--border)] bg-[#FCFCFC] px-[8px] dark:bg-[var(--surface-2)]'
title={activeWorkspace?.name || 'Loading...'}
disabled
>
<span className='truncate font-medium text-[14px] text-[var(--text-primary)]'>
<div className='flex h-[20px] w-[20px] flex-shrink-0 items-center justify-center rounded-[4px] bg-[var(--surface-7)] font-medium text-[12px] text-[var(--text-secondary)] leading-none'>
{workspaceInitial}
</div>
<span className='min-w-0 flex-1 truncate text-left font-base text-[14px] text-[var(--text-primary)]'>
{activeWorkspace?.name || 'Loading...'}
</span>
<ChevronDown className='h-[8px] w-[10px] flex-shrink-0 text-[var(--text-muted)]' />
@@ -546,8 +558,8 @@ export function WorkspaceHeader({
<ModalBody>
<p className='text-[12px] text-[var(--text-secondary)]'>
Are you sure you want to leave{' '}
<span className='font-medium text-[var(--text-primary)]'>{leaveTarget?.name}</span>?
You will lose access to all workflows and data in this workspace.{' '}
<span className='font-base text-[var(--text-primary)]'>{leaveTarget?.name}</span>? You
will lose access to all workflows and data in this workspace.{' '}
<span className='text-[var(--text-error)]'>This action cannot be undone.</span>
</p>
</ModalBody>

View File

@@ -582,13 +582,13 @@ export const Sidebar = memo(function Sidebar() {
<>
<aside
ref={sidebarRef}
className='sidebar-container relative h-full overflow-hidden bg-[#F4F4F4] dark:bg-[var(--surface-1)]'
className='sidebar-container relative h-full overflow-hidden bg-[var(--surface-1)]'
aria-label='Workspace sidebar'
onClick={handleSidebarClick}
>
<div className='flex h-full flex-col pt-[12px]'>
{/* Header */}
<div className='flex-shrink-0 px-[16px]'>
<div className='flex-shrink-0 px-[8px]'>
<WorkspaceHeader
activeWorkspace={activeWorkspace}
workspaceId={workspaceId}
@@ -611,16 +611,17 @@ export const Sidebar = memo(function Sidebar() {
</div>
{/* Top Navigation: Home, Search */}
<div className='mt-[14px] flex flex-shrink-0 flex-col gap-[2px] px-[8px]'>
<div className='mt-[10px] flex flex-shrink-0 flex-col gap-[2px] px-[8px]'>
{topNavItems.map((item) => {
const Icon = item.icon
const active = item.href ? pathname?.startsWith(item.href) : false
const baseClasses =
'group flex h-[28px] items-center gap-[8px] rounded-[8px] mx-[2px] px-[8px] text-[14px] hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]'
const activeClasses = active ? 'bg-[var(--surface-6)] dark:bg-[var(--surface-5)]' : ''
'group flex h-[30px] items-center gap-[8px] rounded-[8px] mx-[2px] px-[8px] text-[14px] hover:bg-[var(--surface-active)]'
const activeClasses = active ? 'bg-[var(--surface-active)]' : ''
const textColor = active
? 'text-[var(--text-primary)]'
: 'text-[var(--text-secondary)]'
const iconColor = active ? 'text-[var(--text-primary)]' : 'text-[var(--text-muted)]'
if (item.onClick) {
return (
@@ -631,8 +632,8 @@ export const Sidebar = memo(function Sidebar() {
className={`${baseClasses} ${activeClasses}`}
onClick={item.onClick}
>
<Icon className={`h-[14px] w-[14px] flex-shrink-0 ${textColor}`} />
<span className={`truncate font-medium ${textColor}`}>{item.label}</span>
<Icon className={`h-[16px] w-[16px] flex-shrink-0 ${iconColor}`} />
<span className={`truncate font-base ${textColor}`}>{item.label}</span>
</button>
)
}
@@ -645,8 +646,8 @@ export const Sidebar = memo(function Sidebar() {
className={`${baseClasses} ${activeClasses}`}
onContextMenu={(e) => handleNavItemContextMenu(e, item.href!)}
>
<Icon className={`h-[14px] w-[14px] flex-shrink-0 ${textColor}`} />
<span className={`truncate font-medium ${textColor}`}>{item.label}</span>
<Icon className={`h-[16px] w-[16px] flex-shrink-0 ${iconColor}`} />
<span className={`truncate font-base ${textColor}`}>{item.label}</span>
</Link>
)
})}
@@ -660,20 +661,19 @@ export const Sidebar = memo(function Sidebar() {
)}
>
<div className='px-[16px] pb-[6px]'>
<div className='font-medium text-[var(--text-tertiary)] text-small'>Workspace</div>
<div className='font-base text-[var(--text-tertiary)] text-small'>Workspace</div>
</div>
<div className='flex flex-col gap-[2px] px-[8px]'>
{workspaceNavItems.map((item) => {
const Icon = item.icon
const active = item.href ? pathname?.startsWith(item.href) : false
const baseClasses =
'group flex h-[28px] items-center gap-[8px] rounded-[8px] mx-[2px] px-[8px] text-[14px] hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]'
const activeClasses = active
? 'bg-[var(--surface-6)] dark:bg-[var(--surface-5)]'
: ''
'group flex h-[30px] items-center gap-[8px] rounded-[8px] mx-[2px] px-[8px] text-[14px] hover:bg-[var(--surface-active)]'
const activeClasses = active ? 'bg-[var(--surface-active)]' : ''
const textColor = active
? 'text-[var(--text-primary)]'
: 'text-[var(--text-secondary)]'
const iconColor = active ? 'text-[var(--text-primary)]' : 'text-[var(--text-muted)]'
return (
<Link
@@ -683,8 +683,8 @@ export const Sidebar = memo(function Sidebar() {
className={`${baseClasses} ${activeClasses}`}
onContextMenu={(e) => handleNavItemContextMenu(e, item.href!)}
>
<Icon className={`h-[14px] w-[14px] flex-shrink-0 ${textColor}`} />
<span className={`truncate font-medium ${textColor}`}>{item.label}</span>
<Icon className={`h-[16px] w-[16px] flex-shrink-0 ${iconColor}`} />
<span className={`truncate font-base ${textColor}`}>{item.label}</span>
</Link>
)
})}
@@ -699,7 +699,7 @@ export const Sidebar = memo(function Sidebar() {
{/* Tasks */}
<div className='flex flex-shrink-0 flex-col'>
<div className='px-[16px]'>
<div className='font-medium text-[var(--text-tertiary)] text-small'>All tasks</div>
<div className='font-base text-[var(--text-tertiary)] text-small'>All tasks</div>
</div>
<div className='mt-[6px] flex flex-col gap-[2px] px-[8px]'>
{tasks.map((task) => {
@@ -707,16 +707,19 @@ export const Sidebar = memo(function Sidebar() {
const textColor = active
? 'text-[var(--text-primary)]'
: 'text-[var(--text-secondary)]'
const iconColor = active
? 'text-[var(--text-primary)]'
: 'text-[var(--text-muted)]'
return (
<Link
key={task.id}
href={task.href}
className={`mx-[2px] flex h-[28px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)] ${active ? 'bg-[var(--surface-6)] dark:bg-[var(--surface-5)]' : ''}`}
className={`mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] hover:bg-[var(--surface-active)] ${active ? 'bg-[var(--surface-active)]' : ''}`}
onContextMenu={(e) => handleTaskContextMenu(e, task.href, task.id)}
>
<Blimp className={`h-[14px] w-[14px] flex-shrink-0 ${textColor}`} />
<div className={`min-w-0 truncate font-medium ${textColor}`}>{task.name}</div>
<Blimp className={`h-[16px] w-[16px] flex-shrink-0 ${iconColor}`} />
<div className={`min-w-0 truncate font-base ${textColor}`}>{task.name}</div>
</Link>
)
})}
@@ -727,9 +730,7 @@ export const Sidebar = memo(function Sidebar() {
<div className='workflows-section mt-[14px] flex flex-col'>
<div className='flex flex-shrink-0 flex-col space-y-[4px] px-[16px]'>
<div className='flex items-center justify-between'>
<div className='font-medium text-[var(--text-tertiary)] text-small'>
Workflows
</div>
<div className='font-base text-[var(--text-tertiary)] text-small'>Workflows</div>
<div className='flex items-center justify-center gap-[8px]'>
<Popover>
<Tooltip.Root>
@@ -737,13 +738,13 @@ export const Sidebar = memo(function Sidebar() {
<PopoverTrigger asChild>
<Button
variant='ghost'
className='h-[18px] w-[18px] rounded-[4px] p-0 hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]'
className='h-[18px] w-[18px] rounded-[4px] p-0 hover:bg-[var(--surface-active)]'
disabled={!canEdit}
>
{isImporting || isCreatingFolder ? (
<Loader className='h-[14px] w-[14px]' animate />
<Loader className='h-[16px] w-[16px]' animate />
) : (
<MoreHorizontal className='h-[14px] w-[14px]' />
<MoreHorizontal className='h-[16px] w-[16px]' />
)}
</Button>
</PopoverTrigger>
@@ -757,14 +758,14 @@ export const Sidebar = memo(function Sidebar() {
onClick={handleImportWorkflow}
disabled={!canEdit || isImporting}
>
<Download className='h-[14px] w-[14px]' />
<Download className='h-[16px] w-[16px]' />
<span>{isImporting ? 'Importing...' : 'Import workflow'}</span>
</PopoverItem>
<PopoverItem
onClick={handleCreateFolder}
disabled={!canEdit || isCreatingFolder}
>
<FolderPlus className='h-[14px] w-[14px]' />
<FolderPlus className='h-[16px] w-[16px]' />
<span>{isCreatingFolder ? 'Creating folder...' : 'Create folder'}</span>
</PopoverItem>
</PopoverContent>
@@ -773,11 +774,11 @@ export const Sidebar = memo(function Sidebar() {
<Tooltip.Trigger asChild>
<Button
variant='ghost'
className='h-[18px] w-[18px] rounded-[4px] p-0 hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]'
className='h-[18px] w-[18px] rounded-[4px] p-0 hover:bg-[var(--surface-active)]'
onClick={handleCreateWorkflow}
disabled={isCreatingWorkflow || !canEdit}
>
<Plus className='h-[14px] w-[14px]' />
<Plus className='h-[16px] w-[16px]' />
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
@@ -819,11 +820,11 @@ export const Sidebar = memo(function Sidebar() {
key={item.id}
type='button'
data-item-id={item.id}
className='group mx-[2px] flex h-[28px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] hover:bg-[var(--surface-6)] dark:hover:bg-[var(--surface-5)]'
className='group mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] hover:bg-[var(--surface-active)]'
onClick={item.onClick}
>
<Icon className='h-[14px] w-[14px] flex-shrink-0 text-[var(--text-secondary)]' />
<span className='truncate font-medium text-[var(--text-secondary)]'>
<Icon className='h-[16px] w-[16px] flex-shrink-0 text-[var(--text-muted)]' />
<span className='truncate font-base text-[var(--text-secondary)]'>
{item.label}
</span>
</button>

View File

@@ -62,7 +62,12 @@ export const getExecutionSummaryServerTool: BaseServerTool<
const clampedLimit = Math.min(Math.max(1, limit), 20)
logger.info('Fetching execution summary', { workspaceId, workflowId, limit: clampedLimit, status })
logger.info('Fetching execution summary', {
workspaceId,
workflowId,
limit: clampedLimit,
status,
})
const conditions: SQL[] = [eq(workflowExecutionLogs.workspaceId, workspaceId)]
@@ -97,8 +102,7 @@ export const getExecutionSummaryServerTool: BaseServerTool<
const summaries: ExecutionSummary[] = rows.map((row) => {
const costData = row.cost as any
const errorMsg =
row.level === 'error' ? extractErrorMessage(row.executionData) : null
const errorMsg = row.level === 'error' ? extractErrorMessage(row.executionData) : null
return {
executionId: row.executionId,

View File

@@ -20,7 +20,6 @@ import {
} from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, count, desc, eq, isNull } from 'drizzle-orm'
import { generateWorkspaceContext } from '@/lib/copilot/workspace-context'
import type { DirEntry, GrepMatch, GrepOptions, ReadResult } from '@/lib/copilot/vfs/operations'
import * as ops from '@/lib/copilot/vfs/operations'
import type { DeploymentData } from '@/lib/copilot/vfs/serializers'
@@ -41,6 +40,7 @@ import {
serializeTaskSession,
serializeWorkflowMeta,
} from '@/lib/copilot/vfs/serializers'
import { generateWorkspaceContext } from '@/lib/copilot/workspace-context'
import { listWorkspaceFiles } from '@/lib/uploads/contexts/workspace'
import { hasWorkflowChanged } from '@/lib/workflows/comparison'
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils'