Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions frontend/src/apis/skillMarket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ export async function searchSkills(params: SearchSkillsParams): Promise<SearchSk
// FastAPI wraps errors in a detail object, check for it first
const detail = errorData.detail
const errorMessage =
detail?.error || detail?.message || errorData.error || errorData.message ||
detail?.error ||
detail?.message ||
errorData.error ||
errorData.message ||
`HTTP ${response.status}: Failed to search skills`
throw new Error(errorMessage)
}
Expand Down Expand Up @@ -199,7 +202,10 @@ export async function downloadSkill(skillKey: string): Promise<Blob> {
// FastAPI wraps errors in a detail object, check for it first
const detail = errorData.detail
const errorMessage =
detail?.error || detail?.message || errorData.error || errorData.message ||
detail?.error ||
detail?.message ||
errorData.error ||
errorData.message ||
`HTTP ${response.status}: Failed to download skill`
throw new Error(errorMessage)
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/(tasks)/chat/ChatPageDesktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export function ChatPageDesktop() {
}

return (
<div className="flex smart-h-screen bg-base text-text-primary box-border">
<div className="chat-typography flex smart-h-screen bg-base text-text-primary box-border">
{/* Collapsed sidebar floating buttons */}
{isCollapsed && (
<CollapsedSidebarButtons onExpand={handleToggleCollapsed} onNewTask={handleNewTask} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/(tasks)/chat/ChatPageMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function ChatPageMobile() {
}

return (
<div className="flex smart-h-screen bg-base text-text-primary box-border">
<div className="chat-typography flex smart-h-screen bg-base text-text-primary box-border">
{/* Mobile sidebar - use TaskSidebar's built-in MobileSidebar component */}
<TaskSidebar
isMobileSidebarOpen={isMobileSidebarOpen}
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,13 @@ body {
background-attachment: fixed;
overflow-x: hidden;
font-family:
'Google Sans Flex',
'Google Sans',
'Noto Sans SC',
'Inter',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
'PingFang SC',
'Noto Sans CJK SC',
'Microsoft YaHei',
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Expand Down
39 changes: 39 additions & 0 deletions frontend/src/app/tasks/tasks.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,42 @@
.animate-slide {
animation: slide-horizontal 1.5s ease-in-out infinite;
}

.chat-typography .wmde-markdown,
.chat-typography .markdown-content,
.chat-typography .enhanced-markdown,
.chat-typography .smart-text-line,
.chat-typography .chat-message-prompt {
font-family:
'Google Sans Flex', 'Google Sans', 'Noto Sans SC', 'Inter', 'PingFang SC', 'Noto Sans CJK SC',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
'Microsoft YaHei', 'Helvetica Neue', sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 28px;
letter-spacing: normal;
}

.chat-typography .smart-text-line {
min-height: 28px;
}

.chat-typography .task-sidebar-typography,
.chat-typography .task-sidebar-typography :not(.font-mono):not(code):not(pre) {
font-family:
'Google Sans Flex', 'Google Sans', 'Noto Sans SC', 'Inter', 'PingFang SC', 'Noto Sans CJK SC',
'Microsoft YaHei', 'Helvetica Neue', sans-serif !important;
}

.chat-typography .task-sidebar-typography .sidebar-conversation-title,
.chat-typography .task-sidebar-typography .sidebar-nav-label {
font-size: 14px;
line-height: 20px;
font-weight: 500;
letter-spacing: normal;
}

.chat-typography .task-sidebar-typography .text-text-primary,
.chat-typography .task-sidebar-typography .sidebar-conversation-title,
.chat-typography .task-sidebar-typography .sidebar-nav-label {
color: rgb(68 71 70) !important;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
16 changes: 13 additions & 3 deletions frontend/src/components/common/SmartUrlRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,21 @@ export function SmartTextLine({
}: SmartTextLineProps) {
// If empty line, return non-breaking space to preserve line height
if (!text) {
return <div className={`text-sm break-all min-h-[1.25em] ${className}`}>{'\u00A0'}</div>
return (
<div className={`smart-text-line text-sm break-all min-h-[1.25em] ${className}`}>
{'\u00A0'}
</div>
)
}

// Detect URLs in the text
const detectedUrls = detectUrls(text)

// If no URLs found, render as plain text
if (detectedUrls.length === 0) {
return <div className={`text-sm break-all min-h-[1.25em] ${className}`}>{text}</div>
return (
<div className={`smart-text-line text-sm break-all min-h-[1.25em] ${className}`}>{text}</div>
)
}

// Build segments: alternating between plain text and URL components
Expand Down Expand Up @@ -330,5 +336,9 @@ export function SmartTextLine({
}
}

return <div className={`text-sm break-all min-h-[1.25em] ${className}`}>{segments}</div>
return (
<div className={`smart-text-line text-sm break-all min-h-[1.25em] ${className}`}>
{segments}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,9 @@ export function SkillListWithScope({ scope, selectedGroup }: SkillListWithScopeP
{/* Go to Market button - only show if skill market is available and has URL */}
{skillMarketInfo.available && skillMarketInfo.marketUrl && (
<Button
onClick={() => window.open(skillMarketInfo.marketUrl, '_blank', 'noopener,noreferrer')}
onClick={() =>
window.open(skillMarketInfo.marketUrl, '_blank', 'noopener,noreferrer')
}
size="sm"
>
<ExternalLink className="w-4 h-4 mr-1" />
Expand All @@ -385,10 +387,7 @@ export function SkillListWithScope({ scope, selectedGroup }: SkillListWithScopeP
)}
{/* Search Skills button - only show if skill market is available */}
{skillMarketInfo.available && (
<Button
onClick={() => setSearchModalOpen(true)}
size="sm"
>
<Button onClick={() => setSearchModalOpen(true)} size="sm">
<Search className="w-4 h-4 mr-1" />
{t('settings:skills.search_skills')}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,7 @@ export default function SkillSearchModal({
className="pl-9"
/>
</div>
<Button
variant="primary"
onClick={() => handleSearch(1)}
disabled={searching}
>
<Button variant="primary" onClick={() => handleSearch(1)} disabled={searching}>
{searching ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Expand Down
57 changes: 32 additions & 25 deletions frontend/src/features/tasks/components/chat/QuickAccessCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,20 @@ export function QuickAccessCards({
const allDisplayTeams: DisplayTeam[] =
quickAccessTeams.length > 0
? quickAccessTeams
.map(qa => {
const fullTeam = filteredTeams.find(t => t.id === qa.id)
if (fullTeam) {
return {
...fullTeam,
is_system: qa.is_system,
recommended_mode: qa.recommended_mode || fullTeam.recommended_mode,
} as DisplayTeam
}
return null
})
.filter((t): t is DisplayTeam => t !== null)
.map(qa => {
const fullTeam = filteredTeams.find(t => t.id === qa.id)
if (fullTeam) {
return {
...fullTeam,
is_system: qa.is_system,
recommended_mode: qa.recommended_mode || fullTeam.recommended_mode,
} as DisplayTeam
}
return null
})
.filter((t): t is DisplayTeam => t !== null)
: // Fallback: show first teams from filtered list if no quick access configured
filteredTeams.map(t => ({ ...t, is_system: false }) as DisplayTeam)
filteredTeams.map(t => ({ ...t, is_system: false }) as DisplayTeam)

// Filter out default team only (keep selected team visible with selection state)
const displayTeams = allDisplayTeams.filter(t => {
Expand Down Expand Up @@ -222,9 +222,10 @@ export function QuickAccessCards({
className={`
group relative flex flex-col justify-center
cursor-pointer transition-all duration-200
${isSelected
? 'border-l-[3px] border-l-primary border-y border-r border-border bg-primary/5'
: 'border border-border bg-base'
${
isSelected
? 'border-l-[3px] border-l-primary border-y border-r border-border bg-primary/5'
: 'border border-border bg-base'
}
${isClicked ? 'clicking-card' : ''}
${isClicked ? 'pointer-events-none' : ''}
Expand All @@ -241,8 +242,9 @@ export function QuickAccessCards({
>
<div className="mb-1 w-full">
<span
className={`block text-[15px] font-semibold leading-5 truncate ${isSelected ? 'text-primary' : 'text-text-primary'
}`}
className={`block text-[15px] font-semibold leading-5 truncate ${
isSelected ? 'text-primary' : 'text-text-primary'
}`}
>
{team.name}
</span>
Expand Down Expand Up @@ -339,8 +341,9 @@ export function QuickAccessCards({
return (
<div
key={team.id}
className={`flex items-center gap-2 px-2 py-2 rounded-md cursor-pointer transition-colors ${isSelected ? 'bg-primary/10' : 'hover:bg-hover'
}`}
className={`flex items-center gap-2 px-2 py-2 rounded-md cursor-pointer transition-colors ${
isSelected ? 'bg-primary/10' : 'hover:bg-hover'
}`}
onClick={() => handleSelectTeamFromMore(team)}
role="button"
tabIndex={0}
Expand All @@ -352,10 +355,11 @@ export function QuickAccessCards({
}}
>
<div
className={`w-4 h-4 rounded border flex items-center justify-center flex-shrink-0 ${isSelected
? 'bg-primary border-primary text-white'
: 'border-border bg-background'
}`}
className={`w-4 h-4 rounded border flex items-center justify-center flex-shrink-0 ${
isSelected
? 'bg-primary border-primary text-white'
: 'border-border bg-background'
}`}
>
{isSelected && <Check className="h-3 w-3" />}
</div>
Expand Down Expand Up @@ -427,7 +431,10 @@ export function QuickAccessCards({
}
`}</style>

<div className="w-full max-w-[820px] mx-auto flex flex-wrap items-center justify-center gap-3 mt-6" data-tour="quick-access-cards">
<div
className="w-full max-w-[820px] mx-auto flex flex-wrap items-center justify-center gap-3 mt-6"
data-tour="quick-access-cards"
>
{teamCardsToShow.map(team => (
<div key={team.id}>{renderTeamCard(team)}</div>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,9 @@ const MessageBubble = memo(
))}
</div>
</div>
{remainingContent && <div className="text-sm break-all">{remainingContent}</div>}
{remainingContent && (
<div className="chat-message-prompt text-sm break-all">{remainingContent}</div>
)}
</div>
)
} catch (e) {
Expand Down Expand Up @@ -1282,7 +1284,9 @@ const MessageBubble = memo(
const [prompt, result] = content.split('${$$}$')
return (
<>
{prompt && <div className="text-sm whitespace-pre-line mb-2">{prompt}</div>}
{prompt && (
<div className="chat-message-prompt text-sm whitespace-pre-line mb-2">{prompt}</div>
)}
{result && renderMarkdownResult(result, prompt)}
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default function DeviceParamSync() {
// Wait for devices to load, then validate and select
if (devices.length === 0) return

const deviceExists = devices.some((d) => d.device_id === deviceId)
const deviceExists = devices.some(d => d.device_id === deviceId)
if (deviceExists) {
setSelectedDeviceId(deviceId)
syncedParamRef.current = deviceId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,11 @@ export default function UnifiedRepositorySelector({
const isLoading = repoLoading || branchLoading

return (
<div className={cn('flex items-center min-w-0', className)} data-tour="unified-repo-selector" data-testid="repo-branch-selector">
<div
className={cn('flex items-center min-w-0', className)}
data-tour="unified-repo-selector"
data-testid="repo-branch-selector"
>
<Popover
open={isOpen}
onOpenChange={open => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default function TaskInlineEdit({
onClick={e => e.stopPropagation()}
disabled={isSaving}
className={cn(
'w-full text-sm text-text-primary leading-tight px-1.5 py-0.5 rounded',
'sidebar-conversation-title w-full text-sm font-medium text-text-primary leading-5 px-1.5 py-0.5 rounded',
'border-2 outline-none transition-colors',
'bg-transparent',
error ? 'border-red-500' : 'border-primary',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ export default function TaskListSection({
}}
/>
) : (
<span className="flex-1 min-w-0 text-sm text-text-primary leading-tight truncate">
<span className="sidebar-conversation-title flex-1 min-w-0 text-sm font-medium text-text-primary leading-5 truncate">
{localTitles[task.id] ?? task.title}
</span>
)}
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/features/tasks/components/sidebar/TaskSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,9 @@ export default function TaskSidebar({
>
<span className="flex items-center">
<Plus className="h-4 w-4 flex-shrink-0" />
<span className="ml-1.5">{t('common:tasks.new_conversation')}</span>
<span className="sidebar-nav-label ml-1.5">
{t('common:tasks.new_conversation')}
</span>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
</span>
<span className="text-text-muted opacity-0 group-hover:opacity-100 transition-opacity">
Expand Down Expand Up @@ -340,7 +342,7 @@ export default function TaskSidebar({
<btn.icon
className={`h-4 w-4 flex-shrink-0 ${btn.isActive ? 'text-primary' : ''}`}
/>
<span className="ml-1.5">{btn.label}</span>
<span className="sidebar-nav-label ml-1.5">{btn.label}</span>
</span>
</Button>
{/* Show "New Task" button on hover when in code mode */}
Expand Down Expand Up @@ -541,7 +543,7 @@ export default function TaskSidebar({
<>
{/* Desktop Sidebar - Hidden on mobile, width controlled by parent ResizableSidebar */}
<div
className="hidden lg:flex lg:flex-col w-full h-full bg-base rounded-3xl shadow-sidebar my-3"
className="task-sidebar-typography hidden lg:flex lg:flex-col w-full h-full bg-base rounded-3xl shadow-sidebar my-3"
style={{ height: 'calc(100% - 24px)' }}
data-tour="task-sidebar"
>
Expand All @@ -556,7 +558,7 @@ export default function TaskSidebar({
hideTitle={true}
data-tour="task-sidebar"
>
{sidebarContent}
<div className="task-sidebar-typography h-full flex flex-col">{sidebarContent}</div>
</MobileSidebar>

{/* History Manage Dialog */}
Expand Down
6 changes: 6 additions & 0 deletions frontend/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ export default {
extend: {
fontFamily: {
sans: [
'"Google Sans Flex"',
'"Google Sans"',
'"Noto Sans SC"',
'"Inter"',
'"PingFang SC"',
'"Noto Sans CJK SC"',
'"Microsoft YaHei"',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
Expand Down
Loading