666ghj commited on
Commit
2c53caf
·
1 Parent(s): f99e6c9

Enhance Step4Report component with tool-specific icons and improved action buttons

Browse files

- Introduced dynamic tool icons and colors based on tool type for better visual representation in tool calls and results.
- Updated action buttons for toggling parameters and views, improving user interaction and layout consistency.
- Refactored the InterviewDisplay component to include result length and enhanced quote formatting for better readability.
- Improved overall styling and layout adjustments for a more cohesive user experience.

frontend/src/components/Step4Report.vue CHANGED
@@ -213,8 +213,40 @@
213
 
214
  <!-- Tool Call -->
215
  <template v-if="log.action === 'tool_call'">
216
- <div class="tool-badge">
217
- <svg class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>
219
  </svg>
220
  {{ getToolDisplayName(log.details?.tool_name) }}
@@ -222,15 +254,13 @@
222
  <div v-if="log.details?.parameters && expandedLogs.has(log.timestamp)" class="tool-params">
223
  <pre>{{ formatParams(log.details.parameters) }}</pre>
224
  </div>
225
- <button v-if="log.details?.parameters" class="expand-toggle" @click.stop="toggleLogExpand(log)">
226
- {{ expandedLogs.has(log.timestamp) ? 'Hide Params' : 'Show Params' }}
227
- </button>
228
  </template>
229
 
230
  <!-- Tool Result -->
231
  <template v-if="log.action === 'tool_result'">
232
  <div class="result-wrapper" :class="'result-' + log.details?.tool_name">
233
- <div class="result-meta">
 
234
  <span class="result-tool">{{ getToolDisplayName(log.details?.tool_name) }}</span>
235
  <span class="result-size">{{ formatResultSize(log.details?.result_length) }}</span>
236
  </div>
@@ -239,7 +269,7 @@
239
  <div v-if="!showRawResult[log.timestamp]" class="result-structured">
240
  <!-- Interview Agents - Special Display -->
241
  <template v-if="log.details?.tool_name === 'interview_agents'">
242
- <InterviewDisplay :result="parseInterview(log.details.result)" />
243
  </template>
244
 
245
  <!-- Insight Forge -->
@@ -267,10 +297,6 @@
267
  <div v-else class="result-raw">
268
  <pre>{{ log.details?.result }}</pre>
269
  </div>
270
-
271
- <button class="toggle-raw" @click.stop="toggleRawResult(log.timestamp)">
272
- {{ showRawResult[log.timestamp] ? 'Structured View' : 'Raw Output' }}
273
- </button>
274
  </div>
275
  </template>
276
 
@@ -295,9 +321,6 @@
295
  <div v-if="expandedLogs.has(log.timestamp) && log.details?.response" class="llm-content">
296
  <pre>{{ log.details.response }}</pre>
297
  </div>
298
- <button v-if="log.details?.response" class="expand-toggle" @click.stop="toggleLogExpand(log)">
299
- {{ expandedLogs.has(log.timestamp) ? 'Hide Response' : 'Show Response' }}
300
- </button>
301
  </template>
302
 
303
  <!-- Report Complete -->
@@ -312,9 +335,27 @@
312
  </template>
313
  </div>
314
 
315
- <!-- Elapsed Time -->
316
- <div class="timeline-footer" v-if="log.elapsed_seconds">
317
- <span class="elapsed-badge">+{{ log.elapsed_seconds.toFixed(1) }}s</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  </div>
319
  </div>
320
  </div>
@@ -375,8 +416,26 @@ const logContent = ref(null)
375
  const showRawResult = reactive({})
376
 
377
  // Toggle functions
378
- const toggleRawResult = (timestamp) => {
 
 
 
 
 
 
379
  showRawResult[timestamp] = !showRawResult[timestamp]
 
 
 
 
 
 
 
 
 
 
 
 
380
  }
381
 
382
  const toggleSectionContent = (idx) => {
@@ -419,17 +478,50 @@ const isLogCollapsed = (log) => {
419
  return false
420
  }
421
 
422
- // Tool display names (without emojis)
423
- const getToolDisplayName = (toolName) => {
424
- const names = {
425
- 'insight_forge': 'Deep Insight',
426
- 'panorama_search': 'Panorama Search',
427
- 'interview_agents': 'Agent Interview',
428
- 'quick_search': 'Quick Search',
429
- 'get_graph_statistics': 'Graph Stats',
430
- 'get_entities_by_type': 'Entity Query'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  }
432
- return names[toolName] || toolName
 
 
 
 
 
 
 
 
 
 
 
433
  }
434
 
435
  // Parse functions
@@ -811,8 +903,24 @@ const PanoramaDisplay = {
811
 
812
  // Interview Display Component - Conversation Style (Q&A Format)
813
  const InterviewDisplay = {
814
- props: ['result'],
815
  setup(props) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
816
  const activeIndex = ref(0)
817
  const expandedAnswers = ref(new Set())
818
  // 为每个问题-回答对维护独立的平台选择状态
@@ -921,7 +1029,9 @@ const InterviewDisplay = {
921
  props.result.totalCount > 0 && h('span', { class: 'stat-item' }, [
922
  h('span', { class: 'stat-value' }, props.result.totalCount),
923
  h('span', { class: 'stat-label' }, 'Total')
924
- ])
 
 
925
  ])
926
  ]),
927
  props.result.topic && h('div', { class: 'header-topic' }, props.result.topic)
@@ -986,19 +1096,21 @@ const InterviewDisplay = {
986
  class: ['platform-btn', { active: currentPlatform === 'twitter' }],
987
  onClick: (e) => { e.stopPropagation(); setPlatformTab(activeIndex.value, qIdx, 'twitter') }
988
  }, [
989
- h('svg', { class: 'platform-icon', viewBox: '0 0 24 24', width: 12, height: 12, fill: 'currentColor' }, [
990
- h('path', { d: 'M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z' })
 
 
991
  ]),
992
- h('span', {}, 'X')
993
  ]),
994
  h('button', {
995
  class: ['platform-btn', { active: currentPlatform === 'reddit' }],
996
  onClick: (e) => { e.stopPropagation(); setPlatformTab(activeIndex.value, qIdx, 'reddit') }
997
  }, [
998
- h('svg', { class: 'platform-icon', viewBox: '0 0 24 24', width: 12, height: 12, fill: 'currentColor' }, [
999
- h('path', { d: 'M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z' })
1000
  ]),
1001
- h('span', {}, 'Reddit')
1002
  ])
1003
  ])
1004
  ]),
@@ -1023,17 +1135,26 @@ const InterviewDisplay = {
1023
  props.result.interviews[activeIndex.value]?.quotes?.length > 0 && h('div', { class: 'quotes-section' }, [
1024
  h('div', { class: 'quotes-header' }, 'Key Quotes'),
1025
  h('div', { class: 'quotes-list' },
1026
- props.result.interviews[activeIndex.value].quotes.slice(0, 3).map((quote, qi) =>
1027
- h('blockquote', { key: qi, class: 'quote-item' }, quote.length > 200 ? quote.substring(0, 200) + '...' : quote)
1028
- )
 
 
 
 
 
 
1029
  )
1030
  ])
1031
  ]),
1032
-
1033
  // Summary Section (Collapsible)
1034
  props.result.summary && h('div', { class: 'summary-section' }, [
1035
  h('div', { class: 'summary-header' }, 'Interview Summary'),
1036
- h('div', { class: 'summary-content' }, props.result.summary.length > 500 ? props.result.summary.substring(0, 500) + '...' : props.result.summary)
 
 
 
1037
  ])
1038
  ])
1039
  }
@@ -2253,11 +2374,24 @@ watch(() => props.reportId, (newId) => {
2253
  }
2254
 
2255
  .timeline-footer {
 
 
 
2256
  margin-top: 10px;
2257
  padding-top: 10px;
2258
  border-top: 1px solid #F3F4F6;
2259
  }
2260
 
 
 
 
 
 
 
 
 
 
 
2261
  .elapsed-badge {
2262
  font-size: 11px;
2263
  color: #6B7280;
@@ -2381,12 +2515,83 @@ watch(() => props.reportId, (newId) => {
2381
  border-radius: 6px;
2382
  font-size: 12px;
2383
  font-weight: 600;
 
2384
  }
2385
 
2386
  .tool-icon {
2387
  flex-shrink: 0;
2388
  }
2389
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2390
  .tool-params {
2391
  margin-top: 10px;
2392
  background: transparent;
@@ -2409,19 +2614,24 @@ watch(() => props.reportId, (newId) => {
2409
  padding: 10px;
2410
  }
2411
 
2412
- .expand-toggle {
2413
- margin-top: 8px;
2414
- background: transparent;
2415
- border: none;
2416
- color: var(--wf-active-text);
 
2417
  font-size: 11px;
2418
  font-weight: 500;
 
2419
  cursor: pointer;
2420
- padding: 0;
 
2421
  }
2422
 
2423
- .expand-toggle:hover {
2424
- text-decoration: underline;
 
 
2425
  }
2426
 
2427
  /* Result Wrapper */
@@ -2480,23 +2690,7 @@ watch(() => props.reportId, (newId) => {
2480
  color: #6B7280;
2481
  }
2482
 
2483
- .toggle-raw {
2484
- margin-top: 10px;
2485
- background: rgba(255,255,255,0.7);
2486
- border: none;
2487
- padding: 4px 10px;
2488
- border-radius: 4px;
2489
- font-size: 10px;
2490
- font-weight: 500;
2491
- color: #6B7280;
2492
- cursor: pointer;
2493
- transition: all 0.2s;
2494
- }
2495
-
2496
- .toggle-raw:hover {
2497
- background: rgba(255,255,255,0.9);
2498
- color: #374151;
2499
- }
2500
 
2501
  /* LLM Response */
2502
  .llm-meta {
@@ -2886,6 +3080,12 @@ watch(() => props.reportId, (newId) => {
2886
  font-size: 12px;
2887
  }
2888
 
 
 
 
 
 
 
2889
  :deep(.interview-display .header-topic) {
2890
  margin-top: 4px;
2891
  font-size: 12px;
@@ -2893,66 +3093,85 @@ watch(() => props.reportId, (newId) => {
2893
  line-height: 1.5;
2894
  }
2895
 
2896
- /* Agent Tabs - Minimal pills */
2897
  :deep(.interview-display .agent-tabs) {
2898
  display: flex;
2899
  gap: 8px;
2900
- padding: 0 0 12px 0;
2901
  background: transparent;
2902
  border-bottom: 1px solid #F3F4F6;
2903
  overflow-x: auto;
 
 
 
2904
  }
2905
 
2906
  :deep(.interview-display .agent-tabs::-webkit-scrollbar) {
2907
- height: 2px;
 
 
 
 
2908
  }
2909
 
2910
  :deep(.interview-display .agent-tabs::-webkit-scrollbar-thumb) {
2911
  background: #E5E7EB;
 
 
 
 
 
2912
  }
2913
 
2914
  :deep(.interview-display .agent-tab) {
2915
  display: flex;
2916
  align-items: center;
2917
  gap: 6px;
2918
- padding: 4px 0;
2919
- background: transparent;
2920
- border: none;
2921
- border-bottom: 2px solid transparent;
2922
- border-radius: 0;
2923
  font-size: 12px;
2924
  font-weight: 500;
2925
- color: #9CA3AF;
2926
  cursor: pointer;
2927
  transition: all 0.15s ease;
2928
  white-space: nowrap;
2929
  }
2930
 
2931
  :deep(.interview-display .agent-tab:hover) {
2932
- color: #6B7280;
 
 
2933
  }
2934
 
2935
  :deep(.interview-display .agent-tab.active) {
2936
- background: transparent;
2937
- border-color: #4F46E5;
2938
- color: #111827;
 
2939
  }
2940
 
2941
  :deep(.interview-display .tab-avatar) {
2942
- width: 16px;
2943
- height: 16px;
2944
  display: flex;
2945
  align-items: center;
2946
  justify-content: center;
2947
- background: #F3F4F6;
2948
  color: #6B7280;
2949
- font-size: 9px;
2950
  font-weight: 700;
2951
  border-radius: 50%;
 
 
 
 
 
2952
  }
2953
 
2954
  :deep(.interview-display .agent-tab.active .tab-avatar) {
2955
- background: #4F46E5;
2956
  color: #FFFFFF;
2957
  }
2958
 
@@ -3235,6 +3454,70 @@ watch(() => props.reportId, (newId) => {
3235
  line-height: 1.6;
3236
  }
3237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3238
  /* Quick Search Display */
3239
  :deep(.quick-search-display) {
3240
  padding: 4px 0;
 
213
 
214
  <!-- Tool Call -->
215
  <template v-if="log.action === 'tool_call'">
216
+ <div class="tool-badge" :class="'tool-' + getToolColor(log.details?.tool_name)">
217
+ <!-- Deep Insight - Lightbulb -->
218
+ <svg v-if="getToolIcon(log.details?.tool_name) === 'lightbulb'" class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
219
+ <path d="M9 18h6M10 22h4M12 2a7 7 0 0 0-4 12.5V17a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-2.5A7 7 0 0 0 12 2z"></path>
220
+ </svg>
221
+ <!-- Panorama Search - Globe -->
222
+ <svg v-else-if="getToolIcon(log.details?.tool_name) === 'globe'" class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
223
+ <circle cx="12" cy="12" r="10"></circle>
224
+ <path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
225
+ </svg>
226
+ <!-- Agent Interview - Users -->
227
+ <svg v-else-if="getToolIcon(log.details?.tool_name) === 'users'" class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
228
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
229
+ <circle cx="9" cy="7" r="4"></circle>
230
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"></path>
231
+ </svg>
232
+ <!-- Quick Search - Zap -->
233
+ <svg v-else-if="getToolIcon(log.details?.tool_name) === 'zap'" class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
234
+ <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
235
+ </svg>
236
+ <!-- Graph Stats - Chart -->
237
+ <svg v-else-if="getToolIcon(log.details?.tool_name) === 'chart'" class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
238
+ <line x1="18" y1="20" x2="18" y2="10"></line>
239
+ <line x1="12" y1="20" x2="12" y2="4"></line>
240
+ <line x1="6" y1="20" x2="6" y2="14"></line>
241
+ </svg>
242
+ <!-- Entity Query - Database -->
243
+ <svg v-else-if="getToolIcon(log.details?.tool_name) === 'database'" class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
244
+ <ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
245
+ <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
246
+ <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
247
+ </svg>
248
+ <!-- Default - Tool -->
249
+ <svg v-else class="tool-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
250
  <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>
251
  </svg>
252
  {{ getToolDisplayName(log.details?.tool_name) }}
 
254
  <div v-if="log.details?.parameters && expandedLogs.has(log.timestamp)" class="tool-params">
255
  <pre>{{ formatParams(log.details.parameters) }}</pre>
256
  </div>
 
 
 
257
  </template>
258
 
259
  <!-- Tool Result -->
260
  <template v-if="log.action === 'tool_result'">
261
  <div class="result-wrapper" :class="'result-' + log.details?.tool_name">
262
+ <!-- Hide result-meta for interview_agents as it's shown in the component header -->
263
+ <div v-if="log.details?.tool_name !== 'interview_agents'" class="result-meta">
264
  <span class="result-tool">{{ getToolDisplayName(log.details?.tool_name) }}</span>
265
  <span class="result-size">{{ formatResultSize(log.details?.result_length) }}</span>
266
  </div>
 
269
  <div v-if="!showRawResult[log.timestamp]" class="result-structured">
270
  <!-- Interview Agents - Special Display -->
271
  <template v-if="log.details?.tool_name === 'interview_agents'">
272
+ <InterviewDisplay :result="parseInterview(log.details.result)" :result-length="log.details?.result_length" />
273
  </template>
274
 
275
  <!-- Insight Forge -->
 
297
  <div v-else class="result-raw">
298
  <pre>{{ log.details?.result }}</pre>
299
  </div>
 
 
 
 
300
  </div>
301
  </template>
302
 
 
321
  <div v-if="expandedLogs.has(log.timestamp) && log.details?.response" class="llm-content">
322
  <pre>{{ log.details.response }}</pre>
323
  </div>
 
 
 
324
  </template>
325
 
326
  <!-- Report Complete -->
 
335
  </template>
336
  </div>
337
 
338
+ <!-- Footer: Elapsed Time + Action Buttons -->
339
+ <div class="timeline-footer" v-if="log.elapsed_seconds || (log.action === 'tool_call' && log.details?.parameters) || log.action === 'tool_result' || (log.action === 'llm_response' && log.details?.response)">
340
+ <span v-if="log.elapsed_seconds" class="elapsed-badge">+{{ log.elapsed_seconds.toFixed(1) }}s</span>
341
+ <span v-else class="elapsed-placeholder"></span>
342
+
343
+ <div class="footer-actions">
344
+ <!-- Tool Call: Show/Hide Params -->
345
+ <button v-if="log.action === 'tool_call' && log.details?.parameters" class="action-btn" @click.stop="toggleLogExpand(log)">
346
+ {{ expandedLogs.has(log.timestamp) ? 'Hide Params' : 'Show Params' }}
347
+ </button>
348
+
349
+ <!-- Tool Result: Raw/Structured View -->
350
+ <button v-if="log.action === 'tool_result'" class="action-btn" @click.stop="toggleRawResult(log.timestamp, $event)">
351
+ {{ showRawResult[log.timestamp] ? 'Structured View' : 'Raw Output' }}
352
+ </button>
353
+
354
+ <!-- LLM Response: Show/Hide Response -->
355
+ <button v-if="log.action === 'llm_response' && log.details?.response" class="action-btn" @click.stop="toggleLogExpand(log)">
356
+ {{ expandedLogs.has(log.timestamp) ? 'Hide Response' : 'Show Response' }}
357
+ </button>
358
+ </div>
359
  </div>
360
  </div>
361
  </div>
 
416
  const showRawResult = reactive({})
417
 
418
  // Toggle functions
419
+ const toggleRawResult = (timestamp, event) => {
420
+ // 保存按钮相对于视口的位置
421
+ const button = event?.target
422
+ const buttonRect = button?.getBoundingClientRect()
423
+ const buttonTopBeforeToggle = buttonRect?.top
424
+
425
+ // 切换状态
426
  showRawResult[timestamp] = !showRawResult[timestamp]
427
+
428
+ // 等待 DOM 更新后,调整滚动位置以保持按钮在相同位置
429
+ if (button && buttonTopBeforeToggle !== undefined && rightPanel.value) {
430
+ nextTick(() => {
431
+ const newButtonRect = button.getBoundingClientRect()
432
+ const buttonTopAfterToggle = newButtonRect.top
433
+ const scrollDelta = buttonTopAfterToggle - buttonTopBeforeToggle
434
+
435
+ // 调整滚动位置
436
+ rightPanel.value.scrollTop += scrollDelta
437
+ })
438
+ }
439
  }
440
 
441
  const toggleSectionContent = (idx) => {
 
478
  return false
479
  }
480
 
481
+ // Tool configurations with display names and colors
482
+ const toolConfig = {
483
+ 'insight_forge': {
484
+ name: 'Deep Insight',
485
+ color: 'purple',
486
+ icon: 'lightbulb' // 灯泡图标 - 代表洞察
487
+ },
488
+ 'panorama_search': {
489
+ name: 'Panorama Search',
490
+ color: 'blue',
491
+ icon: 'globe' // 地球图标 - 代表全景搜索
492
+ },
493
+ 'interview_agents': {
494
+ name: 'Agent Interview',
495
+ color: 'green',
496
+ icon: 'users' // 用户图标 - 代表对话
497
+ },
498
+ 'quick_search': {
499
+ name: 'Quick Search',
500
+ color: 'orange',
501
+ icon: 'zap' // 闪电图标 - 代表快速
502
+ },
503
+ 'get_graph_statistics': {
504
+ name: 'Graph Stats',
505
+ color: 'cyan',
506
+ icon: 'chart' // 图表图标 - 代表统计
507
+ },
508
+ 'get_entities_by_type': {
509
+ name: 'Entity Query',
510
+ color: 'pink',
511
+ icon: 'database' // 数据库图标 - 代表实体
512
  }
513
+ }
514
+
515
+ const getToolDisplayName = (toolName) => {
516
+ return toolConfig[toolName]?.name || toolName
517
+ }
518
+
519
+ const getToolColor = (toolName) => {
520
+ return toolConfig[toolName]?.color || 'gray'
521
+ }
522
+
523
+ const getToolIcon = (toolName) => {
524
+ return toolConfig[toolName]?.icon || 'tool'
525
  }
526
 
527
  // Parse functions
 
903
 
904
  // Interview Display Component - Conversation Style (Q&A Format)
905
  const InterviewDisplay = {
906
+ props: ['result', 'resultLength'],
907
  setup(props) {
908
+ // Format result size for display
909
+ const formatSize = (length) => {
910
+ if (!length) return ''
911
+ if (length >= 1000) {
912
+ return `${(length / 1000).toFixed(1)}k chars`
913
+ }
914
+ return `${length} chars`
915
+ }
916
+
917
+ // Clean quote text - remove leading list numbers to avoid double numbering
918
+ const cleanQuoteText = (text) => {
919
+ if (!text) return ''
920
+ // Remove leading patterns like "1. ", "2. ", "1、", "(1)", "(1)" etc.
921
+ return text.replace(/^\s*\d+[\.\、\))]\s*/, '').trim()
922
+ }
923
+
924
  const activeIndex = ref(0)
925
  const expandedAnswers = ref(new Set())
926
  // 为每个问题-回答对维护独立的平台选择状态
 
1029
  props.result.totalCount > 0 && h('span', { class: 'stat-item' }, [
1030
  h('span', { class: 'stat-value' }, props.result.totalCount),
1031
  h('span', { class: 'stat-label' }, 'Total')
1032
+ ]),
1033
+ props.resultLength && h('span', { class: 'stat-divider' }, '·'),
1034
+ props.resultLength && h('span', { class: 'stat-size' }, formatSize(props.resultLength))
1035
  ])
1036
  ]),
1037
  props.result.topic && h('div', { class: 'header-topic' }, props.result.topic)
 
1096
  class: ['platform-btn', { active: currentPlatform === 'twitter' }],
1097
  onClick: (e) => { e.stopPropagation(); setPlatformTab(activeIndex.value, qIdx, 'twitter') }
1098
  }, [
1099
+ h('svg', { class: 'platform-icon', viewBox: '0 0 24 24', width: 12, height: 12, fill: 'none', stroke: 'currentColor', 'stroke-width': 2 }, [
1100
+ h('circle', { cx: '12', cy: '12', r: '10' }),
1101
+ h('line', { x1: '2', y1: '12', x2: '22', y2: '12' }),
1102
+ h('path', { d: 'M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z' })
1103
  ]),
1104
+ h('span', {}, '世界1')
1105
  ]),
1106
  h('button', {
1107
  class: ['platform-btn', { active: currentPlatform === 'reddit' }],
1108
  onClick: (e) => { e.stopPropagation(); setPlatformTab(activeIndex.value, qIdx, 'reddit') }
1109
  }, [
1110
+ h('svg', { class: 'platform-icon', viewBox: '0 0 24 24', width: 12, height: 12, fill: 'none', stroke: 'currentColor', 'stroke-width': 2 }, [
1111
+ h('path', { d: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z' })
1112
  ]),
1113
+ h('span', {}, '世界2')
1114
  ])
1115
  ])
1116
  ]),
 
1135
  props.result.interviews[activeIndex.value]?.quotes?.length > 0 && h('div', { class: 'quotes-section' }, [
1136
  h('div', { class: 'quotes-header' }, 'Key Quotes'),
1137
  h('div', { class: 'quotes-list' },
1138
+ props.result.interviews[activeIndex.value].quotes.slice(0, 3).map((quote, qi) => {
1139
+ const cleanedQuote = cleanQuoteText(quote)
1140
+ const displayQuote = cleanedQuote.length > 200 ? cleanedQuote.substring(0, 200) + '...' : cleanedQuote
1141
+ return h('blockquote', {
1142
+ key: qi,
1143
+ class: 'quote-item',
1144
+ innerHTML: renderMarkdown(displayQuote)
1145
+ })
1146
+ })
1147
  )
1148
  ])
1149
  ]),
1150
+
1151
  // Summary Section (Collapsible)
1152
  props.result.summary && h('div', { class: 'summary-section' }, [
1153
  h('div', { class: 'summary-header' }, 'Interview Summary'),
1154
+ h('div', {
1155
+ class: 'summary-content',
1156
+ innerHTML: renderMarkdown(props.result.summary.length > 500 ? props.result.summary.substring(0, 500) + '...' : props.result.summary)
1157
+ })
1158
  ])
1159
  ])
1160
  }
 
2374
  }
2375
 
2376
  .timeline-footer {
2377
+ display: flex;
2378
+ justify-content: space-between;
2379
+ align-items: center;
2380
  margin-top: 10px;
2381
  padding-top: 10px;
2382
  border-top: 1px solid #F3F4F6;
2383
  }
2384
 
2385
+ .elapsed-placeholder {
2386
+ flex-shrink: 0;
2387
+ }
2388
+
2389
+ .footer-actions {
2390
+ display: flex;
2391
+ gap: 8px;
2392
+ margin-left: auto;
2393
+ }
2394
+
2395
  .elapsed-badge {
2396
  font-size: 11px;
2397
  color: #6B7280;
 
2515
  border-radius: 6px;
2516
  font-size: 12px;
2517
  font-weight: 600;
2518
+ transition: all 0.2s ease;
2519
  }
2520
 
2521
  .tool-icon {
2522
  flex-shrink: 0;
2523
  }
2524
 
2525
+ /* Tool Colors - Purple (Deep Insight) */
2526
+ .tool-badge.tool-purple {
2527
+ background: linear-gradient(135deg, #F5F3FF 0%, #EDE9FE 100%);
2528
+ border-color: #C4B5FD;
2529
+ color: #6D28D9;
2530
+ }
2531
+ .tool-badge.tool-purple .tool-icon {
2532
+ stroke: #7C3AED;
2533
+ }
2534
+
2535
+ /* Tool Colors - Blue (Panorama Search) */
2536
+ .tool-badge.tool-blue {
2537
+ background: linear-gradient(135deg, #EFF6FF 0%, #DBEAFE 100%);
2538
+ border-color: #93C5FD;
2539
+ color: #1D4ED8;
2540
+ }
2541
+ .tool-badge.tool-blue .tool-icon {
2542
+ stroke: #2563EB;
2543
+ }
2544
+
2545
+ /* Tool Colors - Green (Agent Interview) */
2546
+ .tool-badge.tool-green {
2547
+ background: linear-gradient(135deg, #F0FDF4 0%, #DCFCE7 100%);
2548
+ border-color: #86EFAC;
2549
+ color: #15803D;
2550
+ }
2551
+ .tool-badge.tool-green .tool-icon {
2552
+ stroke: #16A34A;
2553
+ }
2554
+
2555
+ /* Tool Colors - Orange (Quick Search) */
2556
+ .tool-badge.tool-orange {
2557
+ background: linear-gradient(135deg, #FFF7ED 0%, #FFEDD5 100%);
2558
+ border-color: #FDBA74;
2559
+ color: #C2410C;
2560
+ }
2561
+ .tool-badge.tool-orange .tool-icon {
2562
+ stroke: #EA580C;
2563
+ }
2564
+
2565
+ /* Tool Colors - Cyan (Graph Stats) */
2566
+ .tool-badge.tool-cyan {
2567
+ background: linear-gradient(135deg, #ECFEFF 0%, #CFFAFE 100%);
2568
+ border-color: #67E8F9;
2569
+ color: #0E7490;
2570
+ }
2571
+ .tool-badge.tool-cyan .tool-icon {
2572
+ stroke: #0891B2;
2573
+ }
2574
+
2575
+ /* Tool Colors - Pink (Entity Query) */
2576
+ .tool-badge.tool-pink {
2577
+ background: linear-gradient(135deg, #FDF2F8 0%, #FCE7F3 100%);
2578
+ border-color: #F9A8D4;
2579
+ color: #BE185D;
2580
+ }
2581
+ .tool-badge.tool-pink .tool-icon {
2582
+ stroke: #DB2777;
2583
+ }
2584
+
2585
+ /* Tool Colors - Gray (Default) */
2586
+ .tool-badge.tool-gray {
2587
+ background: linear-gradient(135deg, #F9FAFB 0%, #F3F4F6 100%);
2588
+ border-color: #D1D5DB;
2589
+ color: #374151;
2590
+ }
2591
+ .tool-badge.tool-gray .tool-icon {
2592
+ stroke: #6B7280;
2593
+ }
2594
+
2595
  .tool-params {
2596
  margin-top: 10px;
2597
  background: transparent;
 
2614
  padding: 10px;
2615
  }
2616
 
2617
+ /* Unified Action Buttons */
2618
+ .action-btn {
2619
+ background: #F3F4F6;
2620
+ border: 1px solid #E5E7EB;
2621
+ padding: 4px 10px;
2622
+ border-radius: 4px;
2623
  font-size: 11px;
2624
  font-weight: 500;
2625
+ color: #6B7280;
2626
  cursor: pointer;
2627
+ transition: all 0.15s ease;
2628
+ white-space: nowrap;
2629
  }
2630
 
2631
+ .action-btn:hover {
2632
+ background: #E5E7EB;
2633
+ color: #374151;
2634
+ border-color: #D1D5DB;
2635
  }
2636
 
2637
  /* Result Wrapper */
 
2690
  color: #6B7280;
2691
  }
2692
 
2693
+ /* Legacy toggle-raw removed - using unified .action-btn */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2694
 
2695
  /* LLM Response */
2696
  .llm-meta {
 
3080
  font-size: 12px;
3081
  }
3082
 
3083
+ :deep(.interview-display .stat-size) {
3084
+ font-size: 11px;
3085
+ color: #9CA3AF;
3086
+ font-family: 'JetBrains Mono', monospace;
3087
+ }
3088
+
3089
  :deep(.interview-display .header-topic) {
3090
  margin-top: 4px;
3091
  font-size: 12px;
 
3093
  line-height: 1.5;
3094
  }
3095
 
3096
+ /* Agent Tabs - Card Style */
3097
  :deep(.interview-display .agent-tabs) {
3098
  display: flex;
3099
  gap: 8px;
3100
+ padding: 0 0 14px 0;
3101
  background: transparent;
3102
  border-bottom: 1px solid #F3F4F6;
3103
  overflow-x: auto;
3104
+ overflow-y: hidden;
3105
+ scrollbar-width: thin;
3106
+ scrollbar-color: #E5E7EB transparent;
3107
  }
3108
 
3109
  :deep(.interview-display .agent-tabs::-webkit-scrollbar) {
3110
+ height: 4px;
3111
+ }
3112
+
3113
+ :deep(.interview-display .agent-tabs::-webkit-scrollbar-track) {
3114
+ background: transparent;
3115
  }
3116
 
3117
  :deep(.interview-display .agent-tabs::-webkit-scrollbar-thumb) {
3118
  background: #E5E7EB;
3119
+ border-radius: 2px;
3120
+ }
3121
+
3122
+ :deep(.interview-display .agent-tabs::-webkit-scrollbar-thumb:hover) {
3123
+ background: #D1D5DB;
3124
  }
3125
 
3126
  :deep(.interview-display .agent-tab) {
3127
  display: flex;
3128
  align-items: center;
3129
  gap: 6px;
3130
+ padding: 6px 12px;
3131
+ background: #F9FAFB;
3132
+ border: 1px solid #E5E7EB;
3133
+ border-radius: 8px;
 
3134
  font-size: 12px;
3135
  font-weight: 500;
3136
+ color: #6B7280;
3137
  cursor: pointer;
3138
  transition: all 0.15s ease;
3139
  white-space: nowrap;
3140
  }
3141
 
3142
  :deep(.interview-display .agent-tab:hover) {
3143
+ background: #F3F4F6;
3144
+ border-color: #D1D5DB;
3145
+ color: #374151;
3146
  }
3147
 
3148
  :deep(.interview-display .agent-tab.active) {
3149
+ background: linear-gradient(135deg, #EEF2FF 0%, #E0E7FF 100%);
3150
+ border-color: #A5B4FC;
3151
+ color: #4338CA;
3152
+ box-shadow: 0 1px 2px rgba(99, 102, 241, 0.1);
3153
  }
3154
 
3155
  :deep(.interview-display .tab-avatar) {
3156
+ width: 18px;
3157
+ height: 18px;
3158
  display: flex;
3159
  align-items: center;
3160
  justify-content: center;
3161
+ background: #E5E7EB;
3162
  color: #6B7280;
3163
+ font-size: 10px;
3164
  font-weight: 700;
3165
  border-radius: 50%;
3166
+ flex-shrink: 0;
3167
+ }
3168
+
3169
+ :deep(.interview-display .agent-tab:hover .tab-avatar) {
3170
+ background: #D1D5DB;
3171
  }
3172
 
3173
  :deep(.interview-display .agent-tab.active .tab-avatar) {
3174
+ background: #6366F1;
3175
  color: #FFFFFF;
3176
  }
3177
 
 
3454
  line-height: 1.6;
3455
  }
3456
 
3457
+ /* Markdown styles in summary */
3458
+ :deep(.interview-display .summary-content h2),
3459
+ :deep(.interview-display .summary-content h3),
3460
+ :deep(.interview-display .summary-content h4),
3461
+ :deep(.interview-display .summary-content h5) {
3462
+ margin: 12px 0 8px 0;
3463
+ font-weight: 600;
3464
+ color: #111827;
3465
+ }
3466
+
3467
+ :deep(.interview-display .summary-content h2) {
3468
+ font-size: 15px;
3469
+ }
3470
+
3471
+ :deep(.interview-display .summary-content h3) {
3472
+ font-size: 14px;
3473
+ }
3474
+
3475
+ :deep(.interview-display .summary-content h4),
3476
+ :deep(.interview-display .summary-content h5) {
3477
+ font-size: 13px;
3478
+ }
3479
+
3480
+ :deep(.interview-display .summary-content p) {
3481
+ margin: 8px 0;
3482
+ }
3483
+
3484
+ :deep(.interview-display .summary-content strong) {
3485
+ font-weight: 600;
3486
+ color: #111827;
3487
+ }
3488
+
3489
+ :deep(.interview-display .summary-content em) {
3490
+ font-style: italic;
3491
+ }
3492
+
3493
+ :deep(.interview-display .summary-content ul),
3494
+ :deep(.interview-display .summary-content ol) {
3495
+ margin: 8px 0;
3496
+ padding-left: 20px;
3497
+ }
3498
+
3499
+ :deep(.interview-display .summary-content li) {
3500
+ margin: 4px 0;
3501
+ }
3502
+
3503
+ :deep(.interview-display .summary-content blockquote) {
3504
+ margin: 8px 0;
3505
+ padding-left: 12px;
3506
+ border-left: 3px solid #E5E7EB;
3507
+ color: #6B7280;
3508
+ font-style: italic;
3509
+ }
3510
+
3511
+ /* Markdown styles in quotes */
3512
+ :deep(.interview-display .quote-item strong) {
3513
+ font-weight: 600;
3514
+ color: #374151;
3515
+ }
3516
+
3517
+ :deep(.interview-display .quote-item em) {
3518
+ font-style: italic;
3519
+ }
3520
+
3521
  /* Quick Search Display */
3522
  :deep(.quick-search-display) {
3523
  padding: 4px 0;