Skip to content

Commit 170b34f

Browse files
authored
Merge pull request #2274 from netease-youdao/fisherdaddy/optimize-mainpage-ui
feat(cowork): add time-aware greeting and recent tasks to home view
2 parents d7a7b2c + e375dc2 commit 170b34f

6 files changed

Lines changed: 103 additions & 6 deletions

File tree

src/renderer/components/cowork/CoworkPromptInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2961,7 +2961,7 @@ const CoworkPromptInput = React.forwardRef<CoworkPromptInputRef, CoworkPromptInp
29612961
{isLarge ? (
29622962
useHomeContextLayout ? (
29632963
<>
2964-
<div className="relative z-10 rounded-2xl border border-border bg-surface shadow-card">
2964+
<div className="relative z-10 rounded-2xl border border-border bg-surface shadow-card transition-[border-color,box-shadow] duration-200 focus-within:border-primary/35 focus-within:shadow-elevated">
29652965
{largeAttachmentPreview}
29662966
{selectedTextSnippetPreview}
29672967
{sessionGoalStatusBar}

src/renderer/components/cowork/CoworkView.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,20 @@ import { useAgentSelectedModel } from './agentModelSelection';
3838
import { CoworkUiEvent } from './constants';
3939
import CoworkPromptInput, { type CoworkPromptInputRef } from './CoworkPromptInput';
4040
import CoworkSessionDetail from './CoworkSessionDetail';
41+
import HomeRecentTasks from './HomeRecentTasks';
4142
import { reportPromptTemplateAction } from './promptAnalytics';
4243
import { buildCoworkContinuationSystemPrompt, buildCoworkSystemPrompt } from './skillSystemPrompt';
4344

45+
// Time-aware hero greeting: the brand mark stays as the logo, so the heading
46+
// can greet the user instead of repeating the product name on every visit.
47+
const resolveHomeGreetingKey = (date: Date = new Date()): string => {
48+
const hour = date.getHours();
49+
if (hour >= 5 && hour < 12) return 'coworkGreetingMorning';
50+
if (hour >= 12 && hour < 18) return 'coworkGreetingAfternoon';
51+
if (hour >= 18 && hour < 23) return 'coworkGreetingEvening';
52+
return 'coworkGreetingLateNight';
53+
};
54+
4455
const logCoworkViewModel = (message: string): void => {
4556
console.debug(`[CoworkView] ${message}`);
4657
window.electron?.log?.fromRenderer?.('debug', 'CoworkView', message);
@@ -795,13 +806,13 @@ const CoworkView: React.FC<CoworkViewProps> = ({ onRequestAppSettings, onShowSki
795806
className="mt-4 text-2xl font-semibold leading-[var(--lobster-leading-2xl)] tracking-normal text-foreground animate-fade-in-up"
796807
style={{ animationDelay: '70ms', animationFillMode: 'both' }}
797808
>
798-
{i18nService.t('coworkWelcome')}
809+
{i18nService.t(resolveHomeGreetingKey())}
799810
</h2>
800811
<p
801812
className="mt-2 text-[length:var(--lobster-text-promptLarge)] font-normal leading-[var(--lobster-leading-promptLarge)] text-secondary animate-fade-in-up"
802813
style={{ animationDelay: '120ms', animationFillMode: 'both' }}
803814
>
804-
{i18nService.t('coworkDescription')}
815+
{i18nService.t('coworkHomeTagline')}
805816
</p>
806817
</div>
807818

@@ -846,6 +857,9 @@ const CoworkView: React.FC<CoworkViewProps> = ({ onRequestAppSettings, onShowSki
846857
)}
847858
<CreditsResetCampaignFloat />
848859
</div>
860+
861+
{/* Recent tasks - one-click resume for returning users */}
862+
<HomeRecentTasks />
849863
<div aria-hidden="true" className="w-full min-h-[24px] flex-[3_0_0px]" />
850864
</div>
851865
</div>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React, { useMemo } from 'react';
2+
import { useSelector } from 'react-redux';
3+
4+
import { coworkService } from '../../services/cowork';
5+
import { i18nService } from '../../services/i18n';
6+
import { RootState } from '../../store';
7+
import { CoworkSessionStatusValue } from '../../types/cowork';
8+
import { formatAgentTaskRelativeTime } from '../agentSidebar/time';
9+
import LoadingIcon from '../icons/LoadingIcon';
10+
11+
const MAX_RECENT_TASKS = 3;
12+
13+
// Quiet resume strip for the home canvas: the sidebar remains the full task
14+
// list; this only surfaces the latest few so returning users can continue
15+
// without leaving the input area.
16+
const HomeRecentTasks: React.FC = () => {
17+
const sessions = useSelector((state: RootState) => state.cowork.sessions);
18+
19+
const recentSessions = useMemo(() => {
20+
return [...sessions]
21+
.sort((a, b) => (b.updatedAt || b.createdAt) - (a.updatedAt || a.createdAt))
22+
.slice(0, MAX_RECENT_TASKS);
23+
}, [sessions]);
24+
25+
if (recentSessions.length === 0) {
26+
return null;
27+
}
28+
29+
return (
30+
<div
31+
className="relative z-0 mt-10 w-full max-w-3xl animate-fade-in-up"
32+
style={{ animationDelay: '320ms', animationFillMode: 'both' }}
33+
>
34+
<div className="mb-2.5 px-0.5 text-xs font-medium text-secondary">
35+
{i18nService.t('coworkRecentTasks')}
36+
</div>
37+
<div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
38+
{recentSessions.map((session) => {
39+
const isRunning = session.status === CoworkSessionStatusValue.Running;
40+
const relativeTime = formatAgentTaskRelativeTime(session.updatedAt || session.createdAt);
41+
42+
return (
43+
<button
44+
key={session.id}
45+
type="button"
46+
onClick={() => void coworkService.loadSession(session.id)}
47+
title={session.title}
48+
className="flex flex-col items-start gap-2 rounded-xl border border-border-subtle bg-surface px-3.5 py-3 text-left transition-all duration-200 ease-out hover:border-border hover:bg-surface-raised hover:shadow-subtle active:scale-[0.99]"
49+
>
50+
<span className="line-clamp-2 min-h-10 w-full text-[13px] font-normal leading-5 text-foreground">
51+
{session.title}
52+
</span>
53+
{isRunning ? (
54+
<span className="inline-flex items-center gap-1.5 text-xs text-secondary">
55+
<LoadingIcon className="h-3 w-3 animate-spin" aria-hidden="true" />
56+
{i18nService.t('myAgentSidebarRunning')}
57+
</span>
58+
) : (
59+
<span className="text-xs text-foreground/45" title={relativeTime.full}>
60+
{relativeTime.compact}
61+
</span>
62+
)}
63+
</button>
64+
);
65+
})}
66+
</div>
67+
</div>
68+
);
69+
};
70+
71+
export default HomeRecentTasks;

src/renderer/components/quick-actions/PromptPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const PromptPanel: React.FC<PromptPanelProps> = ({ action, onPromptSelect }) =>
5151
${
5252
isPromptSelected
5353
? 'dark:bg-primary-muted bg-primary-muted border-[color-mix(in_srgb,var(--lobster-primary)_50%,transparent)]'
54-
: 'bg-surface border-border hover:border-border hover:border-border hover:bg-surface-raised'
54+
: 'bg-surface border-border hover:border-primary/30 hover:bg-surface-raised'
5555
}
5656
`}
5757
>

src/renderer/components/quick-actions/QuickActionBar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ const QuickActionBar: React.FC<QuickActionBarProps> = ({ actions, onActionSelect
3636
key={action.id}
3737
type="button"
3838
onClick={() => onActionSelect(action.id)}
39-
className="flex items-center gap-1.5 rounded-lg border border-border-subtle bg-surface px-3 py-1.5 text-[length:var(--lobster-text-sidebarCompact)] font-normal leading-5 text-secondary transition-all duration-200 ease-out hover:bg-surface-raised hover:border-primary/30 hover:text-foreground"
39+
className="group flex items-center gap-1.5 rounded-full border border-border-subtle bg-surface px-3.5 py-1.5 text-[length:var(--lobster-text-sidebarCompact)] font-normal leading-5 text-secondary transition-all duration-200 ease-out hover:-translate-y-px hover:border-primary/30 hover:bg-surface-raised hover:text-foreground hover:shadow-subtle active:translate-y-0 active:scale-[0.97]"
4040
>
4141
{IconComponent && (
42-
<IconComponent className="h-3.5 w-3.5 text-secondary" />
42+
<IconComponent className="h-3.5 w-3.5 text-secondary transition-colors duration-200 group-hover:text-primary" />
4343
)}
4444
<span>{action.label}</span>
4545
</button>

src/renderer/services/i18n.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,12 @@ const translations: Record<LanguageType, Record<string, string>> = {
10921092
coworkQuestionWizardAnswerRequired: '请选择或输入答案',
10931093
coworkWelcome: 'LobsterAI',
10941094
coworkDescription: '全场景办公助手 Agent',
1095+
coworkGreetingMorning: '早上好',
1096+
coworkGreetingAfternoon: '下午好',
1097+
coworkGreetingEvening: '晚上好',
1098+
coworkGreetingLateNight: '夜深了',
1099+
coworkHomeTagline: '我是 LobsterAI,你的全场景办公 Agent,把任务交给我吧',
1100+
coworkRecentTasks: '最近任务',
10951101
coworkCurrentAgent: '当前 Agent',
10961102
coworkSelectAgent: '选择 Agent',
10971103

@@ -3814,6 +3820,12 @@ const translations: Record<LanguageType, Record<string, string>> = {
38143820
coworkQuestionWizardAnswerRequired: 'Please select or enter an answer',
38153821
coworkWelcome: 'LobsterAI',
38163822
coworkDescription: 'All-scenario office assistant Agent',
3823+
coworkGreetingMorning: 'Good morning',
3824+
coworkGreetingAfternoon: 'Good afternoon',
3825+
coworkGreetingEvening: 'Good evening',
3826+
coworkGreetingLateNight: 'Up late?',
3827+
coworkHomeTagline: 'LobsterAI here, your all-scenario office agent — hand me any task',
3828+
coworkRecentTasks: 'Recent tasks',
38173829
coworkCurrentAgent: 'Current Agent',
38183830
coworkSelectAgent: 'Select Agent',
38193831

0 commit comments

Comments
 (0)