Skip to content

Commit d615c89

Browse files
authored
feat: Frontend optimization and enhance OpenTelemetry tracing (#133)
Squash merge PR #133 from yhjun1026:main This PR includes: - Frontend responsive layout optimizations for mobile/tablet - Skill selection feature with UI chips in chat - Type-safe SelectedSkill interface implementation - Simplified OpenTelemetry tracing implementation Frontend improvements: - Added responsive breakpoints (small/medium/large screens) - Toggle between editor and chat panels on small screens - Dynamic column widths based on screen size - Better UX for mobile and tablet users - Agent header styling improvements (flex-shrink, text truncation) New Features: - Skill selection UI in chat input - Skills persisted in localStorage - Skills passed via chat_in_params to backend Co-authored-by: yhjun1026 <yhjun1026@users.noreply.github.com>
1 parent de7fc8d commit d615c89

9 files changed

Lines changed: 457 additions & 813 deletions

File tree

packages/derisk-core/src/derisk/util/tracer/opentelemetry.py

Lines changed: 73 additions & 627 deletions
Large diffs are not rendered by default.

web/src/app/application/app/components/agent-header.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ export default function AgentHeader({ activeTab, onTabChange }: AgentHeaderProps
8686
return (
8787
<div className="bg-white/80 backdrop-blur-xl rounded-t-2xl border-b border-gray-100/60">
8888
{/* Top bar: agent info + actions */}
89-
<div className="flex items-center justify-between px-5 py-3">
90-
<div className="flex items-center gap-3">
91-
<div className="w-10 h-10 rounded-xl overflow-hidden ring-2 ring-white shadow-lg shadow-gray-200/50">
89+
<div className="flex items-center justify-between px-5 py-3 gap-3">
90+
<div className="flex items-center gap-3 min-w-0 flex-1">
91+
<div className="w-10 h-10 rounded-xl overflow-hidden ring-2 ring-white shadow-lg shadow-gray-200/50 flex-shrink-0">
9292
<Image
9393
src={appInfo?.icon || '/agents/agent1.jpg'}
9494
alt="Agent Icon"
@@ -97,19 +97,19 @@ export default function AgentHeader({ activeTab, onTabChange }: AgentHeaderProps
9797
className="object-cover"
9898
/>
9999
</div>
100-
<div className="flex flex-col">
101-
<div className="font-semibold text-gray-800 text-[15px] leading-tight tracking-[-0.01em]">
100+
<div className="flex flex-col min-w-0">
101+
<div className="font-semibold text-gray-800 text-[15px] leading-tight tracking-[-0.01em] truncate">
102102
{appInfo?.app_name || 'Untitled Agent'}
103103
</div>
104104
{appInfo?.app_describe && (
105-
<div className="text-[12px] text-gray-400 mt-0.5 truncate max-w-[300px]">
105+
<div className="text-[12px] text-gray-400 mt-0.5 truncate">
106106
{appInfo.app_describe}
107107
</div>
108108
)}
109109
</div>
110110
</div>
111111

112-
<div className="flex items-center gap-2.5">
112+
<div className="flex items-center gap-2.5 flex-shrink-0">
113113
{appInfo?.config_version && (
114114
<Dropdown menu={{ items: versionItems, onClick: handleMenuClick }} trigger={['click']}>
115115
<Tag className="cursor-pointer m-0 border-0 bg-gradient-to-r from-gray-50 to-gray-100 hover:from-gray-100 hover:to-gray-150 text-gray-600 rounded-lg px-3 py-1 text-[12px] font-medium flex items-center gap-1.5 transition-all shadow-sm hover:shadow-md">
@@ -132,12 +132,12 @@ export default function AgentHeader({ activeTab, onTabChange }: AgentHeaderProps
132132
</div>
133133

134134
{/* Tab bar + status */}
135-
<div className="flex items-center justify-between px-5 border-t border-gray-50/80">
136-
<div className="flex items-center gap-0">
135+
<div className="flex items-center justify-between px-5 border-t border-gray-50/80 gap-3">
136+
<div className="flex items-center gap-0 overflow-x-auto flex-1 min-w-0">
137137
{tabs.map(tab => (
138138
<button
139139
key={tab.key}
140-
className={`px-4 py-2.5 text-[13px] font-medium transition-all duration-200 border-b-2 relative ${
140+
className={`px-4 py-2.5 text-[13px] font-medium transition-all duration-200 border-b-2 relative whitespace-nowrap flex-shrink-0 ${
141141
activeTab === tab.key
142142
? 'text-blue-600 border-blue-500'
143143
: 'text-gray-500 border-transparent hover:text-gray-700 hover:border-gray-200'
@@ -148,7 +148,7 @@ export default function AgentHeader({ activeTab, onTabChange }: AgentHeaderProps
148148
</button>
149149
))}
150150
</div>
151-
<div className="flex items-center gap-2.5 text-[11px] py-2">
151+
<div className="flex items-center gap-2.5 text-[11px] py-2 flex-shrink-0">
152152
{fetchUpdateAppLoading ? (
153153
<span className="flex items-center gap-1.5 text-blue-500 font-medium">
154154
<ClockCircleOutlined spin className="text-[10px]" /> Saving...

web/src/app/application/app/components/chat-content.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AppContext, ChatContentContext } from '@/contexts';
1+
import { AppContext, ChatContentContext, SelectedSkill } from '@/contexts';
22
import { ChartData, ChatHistoryResponse, UserChatContent} from '@/types/chat';
33
import { useContext, useState, useRef, useCallback, useEffect } from 'react';
44
import { Layout } from 'antd';
@@ -23,6 +23,7 @@ function ChatContent() {
2323
sub_type: string;
2424
}>>([]);
2525
const [modelValue, setModelValue] = useState<string>('');
26+
const [selectedSkills, setSelectedSkills] = useState<SelectedSkill[]>([]);
2627
const scrollRef = useRef<HTMLDivElement>(null);
2728
const [currentDialogue] = useState<any>(null);
2829

@@ -213,8 +214,10 @@ function ChatContent() {
213214
maxNewTokensValue,
214215
resourceValue,
215216
modelValue,
217+
selectedSkills,
216218
setModelValue,
217219
setResourceValue,
220+
setSelectedSkills,
218221
setTemperatureValue,
219222
setMaxNewTokensValue,
220223
setChatInParams,

web/src/app/application/app/page.tsx

Lines changed: 156 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { apiInterceptors, getAppInfo, newDialogue, updateApp, getAppVersion } fr
44
import { AppContext } from '@/contexts';
55
import { IApp } from '@/types/app';
66
import { useRequest } from 'ahooks';
7-
import { Spin, App } from 'antd';
8-
import { useCallback, useRef, useState } from 'react';
7+
import { Spin, App, Tooltip } from 'antd';
8+
import { useCallback, useRef, useState, useEffect } from 'react';
99
import { useTranslation } from 'react-i18next';
1010
import AgentList from './components/agent-list';
1111
import AgentHeader from './components/agent-header';
@@ -16,7 +16,7 @@ import TabTools from './components/tab-tools';
1616
import TabAgents from './components/tab-agents';
1717
import TabKnowledge from './components/tab-knowledge';
1818
import ChatContent from './components/chat-content';
19-
import { AppstoreOutlined } from '@ant-design/icons';
19+
import { AppstoreOutlined, EditOutlined, MessageOutlined } from '@ant-design/icons';
2020

2121
export default function AgentBuilder() {
2222
const { message, notification } = App.useApp();
@@ -25,13 +25,36 @@ export default function AgentBuilder() {
2525
// Agent selection
2626
const [selectedAppCode, setSelectedAppCode] = useState<string | null>(null);
2727
const [activeTab, setActiveTab] = useState('overview');
28+
29+
// Panel toggle for small screens: 'editor' | 'chat'
30+
const [activePanel, setActivePanel] = useState<'editor' | 'chat'>('editor');
31+
const [screenSize, setScreenSize] = useState<'small' | 'medium' | 'large'>('large');
2832

2933
// AppContext state (mirrors structure/page.tsx)
3034
const [collapsed, setCollapsed] = useState(false);
3135
const [appInfo, setAppInfo] = useState<any>({});
3236
const [versionData, setVersionData] = useState<any>(null);
3337
const [chatId, setChatId] = useState<string>('');
3438

39+
// Detect screen size
40+
useEffect(() => {
41+
const checkScreenSize = () => {
42+
const width = window.innerWidth;
43+
if (width < 1280) {
44+
setScreenSize('small');
45+
} else if (width < 1536) {
46+
setScreenSize('medium');
47+
} else {
48+
setScreenSize('large');
49+
}
50+
};
51+
checkScreenSize();
52+
window.addEventListener('resize', checkScreenSize);
53+
return () => window.removeEventListener('resize', checkScreenSize);
54+
}, []);
55+
56+
const isSmallScreen = screenSize === 'small';
57+
3558
// Query agent info
3659
const {
3760
run: queryAppInfo,
@@ -166,64 +189,145 @@ export default function AgentBuilder() {
166189
>
167190
<div className="flex h-screen w-full bg-gradient-to-br from-slate-50 via-gray-50 to-blue-50/30 overflow-hidden">
168191
{/* Column 1: Agent List */}
169-
<div className="w-[280px] flex-shrink-0 p-3 pr-0">
192+
<div className={`flex-shrink-0 p-3 pr-0 ${
193+
screenSize === 'small' ? 'w-[200px]' :
194+
screenSize === 'medium' ? 'w-[240px]' :
195+
'w-[280px]'
196+
}`}>
170197
<AgentList selectedAppCode={selectedAppCode} onSelect={handleSelectAgent} onListLoaded={handleListLoaded} />
171198
</div>
172199

173-
{/* Column 2: Config Tabs — collapsible */}
174-
<div
175-
className={`flex-shrink-0 p-3 flex flex-col transition-all duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] overflow-hidden ${
176-
collapsed
177-
? 'w-0 min-w-0 opacity-0 p-0 pointer-events-none'
178-
: 'flex-1 min-w-[320px] opacity-100'
179-
}`}
180-
>
181-
{selectedAppCode && appInfo?.app_code ? (
182-
<Spin spinning={refreshAppInfoLoading} wrapperClassName="flex-1 flex flex-col overflow-hidden">
183-
<div className="flex flex-col h-full bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)] overflow-hidden">
184-
<AgentHeader activeTab={activeTab} onTabChange={setActiveTab} />
185-
<div className="flex-1 overflow-y-auto">
186-
{renderTabContent()}
200+
{/* Column 2 & 3: Editor and Chat - responsive layout */}
201+
{isSmallScreen ? (
202+
/* Small screen: toggle between editor and chat */
203+
<div className="flex-1 flex flex-col min-w-0 p-3 pl-0">
204+
{/* Toggle buttons */}
205+
<div className="flex items-center gap-2 mb-2">
206+
<button
207+
onClick={() => setActivePanel('editor')}
208+
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${
209+
activePanel === 'editor'
210+
? 'bg-blue-500 text-white shadow-md'
211+
: 'bg-white/80 text-gray-600 hover:bg-gray-100 border border-gray-200'
212+
}`}
213+
>
214+
<EditOutlined />
215+
{t('builder_edit', '编辑')}
216+
</button>
217+
<button
218+
onClick={() => setActivePanel('chat')}
219+
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${
220+
activePanel === 'chat'
221+
? 'bg-blue-500 text-white shadow-md'
222+
: 'bg-white/80 text-gray-600 hover:bg-gray-100 border border-gray-200'
223+
}`}
224+
>
225+
<MessageOutlined />
226+
{t('builder_preview', '预览')}
227+
</button>
228+
</div>
229+
230+
{/* Content area */}
231+
<div className="flex-1 min-h-0">
232+
{activePanel === 'editor' ? (
233+
selectedAppCode && appInfo?.app_code ? (
234+
<Spin spinning={refreshAppInfoLoading} wrapperClassName="h-full">
235+
<div className="flex flex-col h-full bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)] overflow-hidden">
236+
<AgentHeader activeTab={activeTab} onTabChange={setActiveTab} />
237+
<div className="flex-1 overflow-y-auto">
238+
{renderTabContent()}
239+
</div>
240+
</div>
241+
</Spin>
242+
) : (
243+
<div className="h-full flex items-center justify-center bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)]">
244+
<div className="text-center">
245+
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-gray-100 to-gray-50 flex items-center justify-center">
246+
<AppstoreOutlined className="text-2xl text-gray-300" />
247+
</div>
248+
<p className="text-gray-400 text-sm font-medium">{t('builder_select_agent')}</p>
249+
</div>
250+
</div>
251+
)
252+
) : (
253+
<div className="h-full bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)] overflow-hidden">
254+
{selectedAppCode && appInfo?.app_code ? (
255+
<ChatContent />
256+
) : (
257+
<div className="flex items-center justify-center h-full">
258+
<div className="text-center">
259+
<div className="w-14 h-14 mx-auto mb-3 rounded-2xl bg-gradient-to-br from-green-50 to-emerald-50 flex items-center justify-center">
260+
<MessageOutlined className="text-2xl text-green-400" />
261+
</div>
262+
<p className="text-gray-400 text-sm font-medium">{t('builder_chat_preview')}</p>
263+
</div>
264+
</div>
265+
)}
187266
</div>
188-
</div>
189-
</Spin>
190-
) : (
191-
<div className="flex-1 flex items-center justify-center bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)]">
192-
<div className="text-center">
193-
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-gray-100 to-gray-50 flex items-center justify-center">
194-
<AppstoreOutlined className="text-2xl text-gray-300" />
267+
)}
268+
</div>
269+
</div>
270+
) : (
271+
/* Large screen: side by side layout */
272+
<>
273+
{/* Column 2: Config Tabs */}
274+
<div
275+
className={`flex-shrink-0 p-3 flex flex-col transition-all duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] overflow-hidden ${
276+
collapsed
277+
? 'w-0 min-w-0 opacity-0 p-0 pointer-events-none'
278+
: 'flex-1 opacity-100'
279+
}`}
280+
style={{ minWidth: collapsed ? 0 : (screenSize === 'medium' ? '360px' : '320px') }}
281+
>
282+
{selectedAppCode && appInfo?.app_code ? (
283+
<Spin spinning={refreshAppInfoLoading} wrapperClassName="flex-1 flex flex-col overflow-hidden">
284+
<div className="flex flex-col h-full bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)] overflow-hidden">
285+
<AgentHeader activeTab={activeTab} onTabChange={setActiveTab} />
286+
<div className="flex-1 overflow-y-auto">
287+
{renderTabContent()}
288+
</div>
289+
</div>
290+
</Spin>
291+
) : (
292+
<div className="flex-1 flex items-center justify-center bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)]">
293+
<div className="text-center">
294+
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-gray-100 to-gray-50 flex items-center justify-center">
295+
<AppstoreOutlined className="text-2xl text-gray-300" />
296+
</div>
297+
<p className="text-gray-400 text-sm font-medium">{t('builder_select_agent')}</p>
298+
<p className="text-gray-300 text-xs mt-1">{t('builder_select_agent')}</p>
299+
</div>
195300
</div>
196-
<p className="text-gray-400 text-sm font-medium">{t('builder_select_agent')}</p>
197-
<p className="text-gray-300 text-xs mt-1">{t('builder_select_agent')}</p>
198-
</div>
301+
)}
199302
</div>
200-
)}
201-
</div>
202303

203-
{/* Column 3: Chat Preview */}
204-
<div
205-
className={`flex-shrink-0 p-3 pl-0 transition-all duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] ${
206-
collapsed ? 'flex-1' : 'w-[480px]'
207-
}`}
208-
>
209-
<div className="h-full bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)] overflow-hidden">
210-
{selectedAppCode && appInfo?.app_code ? (
211-
<ChatContent />
212-
) : (
213-
<div className="flex items-center justify-center h-full">
214-
<div className="text-center">
215-
<div className="w-14 h-14 mx-auto mb-3 rounded-2xl bg-gradient-to-br from-green-50 to-emerald-50 flex items-center justify-center">
216-
<svg className="w-6 h-6 text-green-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
217-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
218-
</svg>
304+
{/* Column 3: Chat Preview */}
305+
<div
306+
className={`flex-shrink-0 p-3 pl-0 transition-all duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] ${
307+
collapsed ? 'flex-1' : ''
308+
}`}
309+
style={{ width: collapsed ? undefined : (screenSize === 'medium' ? '360px' : '480px') }}
310+
>
311+
<div className="h-full bg-white/80 backdrop-blur-xl rounded-2xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.06)] overflow-hidden">
312+
{selectedAppCode && appInfo?.app_code ? (
313+
<ChatContent />
314+
) : (
315+
<div className="flex items-center justify-center h-full">
316+
<div className="text-center">
317+
<div className="w-14 h-14 mx-auto mb-3 rounded-2xl bg-gradient-to-br from-green-50 to-emerald-50 flex items-center justify-center">
318+
<svg className="w-6 h-6 text-green-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
319+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
320+
</svg>
321+
</div>
322+
<p className="text-gray-400 text-sm font-medium">{t('builder_chat_preview')}</p>
323+
<p className="text-gray-300 text-xs mt-1">{t('builder_chat_preview_desc')}</p>
324+
</div>
219325
</div>
220-
<p className="text-gray-400 text-sm font-medium">{t('builder_chat_preview')}</p>
221-
<p className="text-gray-300 text-xs mt-1">{t('builder_chat_preview_desc')}</p>
222-
</div>
326+
)}
223327
</div>
224-
)}
225-
</div>
226-
</div>
328+
</div>
329+
</>
330+
)}
227331
</div>
228332
</AppContext.Provider>
229333
);

0 commit comments

Comments
 (0)