Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
8 changes: 7 additions & 1 deletion 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:
'PingFang SC',
'Google Sans Flex',
'Google Sans',
'Noto Sans Simplified Chinese',
'Noto Sans SC',
'Inter',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
'Noto Sans CJK SC',
'Microsoft YaHei',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/app/tasks/tasks.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,21 @@
.animate-slide {
animation: slide-horizontal 1.5s ease-in-out infinite;
}

.wmde-markdown,
.markdown-content,
.enhanced-markdown,
.smart-text-line,
.chat-message-prompt {
font-family:
'Google Sans Flex', 'Google Sans', 'Noto Sans Simplified Chinese', 'Noto Sans SC', 'Inter',
'Noto Sans CJK SC', 'Microsoft YaHei', 'Helvetica Neue', sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 28px;
letter-spacing: normal;
}

.smart-text-line {
min-height: 28px;
}
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',
'w-full text-sm font-medium text-[#444746] 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="flex-1 min-w-0 text-sm font-medium text-[#444746] leading-5 truncate">
{localTitles[task.id] ?? task.title}
</span>
)}
Expand Down
14 changes: 11 additions & 3 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="ml-1.5 text-[14px] leading-5 font-medium text-[#444746]">
{t('common:tasks.new_conversation')}
</span>
</span>
<span className="text-text-muted opacity-0 group-hover:opacity-100 transition-opacity">
Expand Down Expand Up @@ -340,7 +342,13 @@ 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={`ml-1.5 text-[14px] leading-5 font-medium ${
btn.isActive ? 'text-primary' : 'text-[#444746]'
}`}
>
{btn.label}
</span>
</span>
</Button>
{/* Show "New Task" button on hover when in code mode */}
Expand Down Expand Up @@ -556,7 +564,7 @@ export default function TaskSidebar({
hideTitle={true}
data-tour="task-sidebar"
>
{sidebarContent}
<div className="h-full flex flex-col">{sidebarContent}</div>
</MobileSidebar>

{/* History Manage Dialog */}
Expand Down
8 changes: 7 additions & 1 deletion 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: [
'"PingFang SC"',
'"Google Sans Flex"',
'"Google Sans"',
'"Noto Sans Simplified Chinese"',
'"Noto Sans SC"',
'"Inter"',
'"Noto Sans CJK SC"',
'"Microsoft YaHei"',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
Expand Down