NeonClary Cursor commited on
Commit
53edcb8
·
1 Parent(s): 112b786

feat(ui): header cleanup + Table View shortcut + post-chat downloads strip

Browse files

- Header: drop the redundant 'Human: ' prefix on the human-participant
button; the sidebar already labels the entry as Human, so the prefix
was just visual noise.
- Header: add a dedicated 'Table View' button that opens the same
Summary Table modal as the Settings menu item, so users don't have
to dig through a dropdown to view it.
- ChatArea: after 'End of Chat', render a row mirroring every item in
the header DownloadMenu (Summary table view, .txt, .md, summary
.csv, full API history). The row stacks vertically on phones for
full-label readability.
- ParticipantSidebar: remove the 'Drag to reorder is not supported
yet...' note; reordering isn't on the roadmap and the message just
cluttered the sidebar header.

Co-authored-by: Cursor <cursoragent@cursor.com>

frontend/src/App.js CHANGED
@@ -872,6 +872,12 @@ export default function App() {
872
  humanSubmitting={humanSubmitting}
873
  onHumanSubmit={handleHumanSubmit}
874
  onHumanSkip={handleHumanSkip}
 
 
 
 
 
 
875
  />
876
  </div>
877
  </main>
 
872
  humanSubmitting={humanSubmitting}
873
  onHumanSubmit={handleHumanSubmit}
874
  onHumanSkip={handleHumanSkip}
875
+ onShowTableView={handleShowTableView}
876
+ onDownloadChatTxt={handleDownloadTxt}
877
+ onDownloadChatMd={handleDownloadMd}
878
+ onDownloadCsvTable={handleDownloadCsvTable}
879
+ onDownloadApiLog={handleDownloadApiLog}
880
+ hasApiLog={!!sessionId}
881
  />
882
  </div>
883
  </main>
frontend/src/components/ChatArea.js CHANGED
@@ -1,4 +1,5 @@
1
  import React, { useMemo } from 'react';
 
2
  import MessageBubble from './MessageBubble';
3
  import OrchestratorMessage from './OrchestratorMessage';
4
  import FailsafePauseBanner from './FailsafePauseBanner';
@@ -10,6 +11,11 @@ import HumanTurnIndicator from './HumanTurnIndicator';
10
  * status banners, and the failsafe-pause continue control. Participant
11
  * coloring is derived from each participant's index in the active
12
  * roster, so colors are stable per-participant for the whole chat.
 
 
 
 
 
13
  */
14
  export default function ChatArea({
15
  messages,
@@ -25,6 +31,12 @@ export default function ChatArea({
25
  humanSubmitting,
26
  onHumanSubmit,
27
  onHumanSkip,
 
 
 
 
 
 
28
  }) {
29
  const speakerIdxFor = useMemo(() => {
30
  const map = {};
@@ -103,6 +115,54 @@ export default function ChatArea({
103
  {stats.count} participant messages &middot; {stats.totalTime}s total generation time
104
  </div>
105
  )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  <FailsafePauseBanner pause={pause} onContinue={onContinuePause} />
107
  {isRunning && statusText && !awaitingHuman && (
108
  <div className="status-bar">
 
1
  import React, { useMemo } from 'react';
2
+ import { Table2, FileText, FileCode, FileSpreadsheet, History } from 'lucide-react';
3
  import MessageBubble from './MessageBubble';
4
  import OrchestratorMessage from './OrchestratorMessage';
5
  import FailsafePauseBanner from './FailsafePauseBanner';
 
11
  * status banners, and the failsafe-pause continue control. Participant
12
  * coloring is derived from each participant's index in the active
13
  * roster, so colors are stable per-participant for the whole chat.
14
+ *
15
+ * After "End of Chat" arrives we also render a download strip below the
16
+ * stats line that mirrors the header DownloadMenu items 1:1 (Summary
17
+ * table view, .txt, .md, .csv, full API log). Per UX request these
18
+ * stack vertically on narrow viewports.
19
  */
20
  export default function ChatArea({
21
  messages,
 
31
  humanSubmitting,
32
  onHumanSubmit,
33
  onHumanSkip,
34
+ onShowTableView,
35
+ onDownloadChatTxt,
36
+ onDownloadChatMd,
37
+ onDownloadCsvTable,
38
+ onDownloadApiLog,
39
+ hasApiLog,
40
  }) {
41
  const speakerIdxFor = useMemo(() => {
42
  const map = {};
 
115
  {stats.count} participant messages &middot; {stats.totalTime}s total generation time
116
  </div>
117
  )}
118
+ {chatEnded && (
119
+ <div className="chat-end-downloads" role="group" aria-label="Conversation downloads">
120
+ <button
121
+ type="button"
122
+ className="btn-sm btn-outline chat-end-download-btn"
123
+ onClick={onShowTableView}
124
+ title="Open the conversation summary table"
125
+ >
126
+ <Table2 size={14} />
127
+ Summary table…
128
+ </button>
129
+ <button
130
+ type="button"
131
+ className="btn-sm btn-outline chat-end-download-btn"
132
+ onClick={onDownloadChatTxt}
133
+ >
134
+ <FileText size={14} />
135
+ Chat as .txt
136
+ </button>
137
+ <button
138
+ type="button"
139
+ className="btn-sm btn-outline chat-end-download-btn"
140
+ onClick={onDownloadChatMd}
141
+ >
142
+ <FileCode size={14} />
143
+ Chat as .md
144
+ </button>
145
+ <button
146
+ type="button"
147
+ className="btn-sm btn-outline chat-end-download-btn"
148
+ onClick={onDownloadCsvTable}
149
+ title="Download the summary table as CSV"
150
+ >
151
+ <FileSpreadsheet size={14} />
152
+ Summary table as .csv
153
+ </button>
154
+ <button
155
+ type="button"
156
+ className="btn-sm btn-outline chat-end-download-btn"
157
+ onClick={onDownloadApiLog}
158
+ disabled={!hasApiLog}
159
+ title="Download the full backend API call history for this session"
160
+ >
161
+ <History size={14} />
162
+ Full API history
163
+ </button>
164
+ </div>
165
+ )}
166
  <FailsafePauseBanner pause={pause} onContinue={onContinuePause} />
167
  {isRunning && statusText && !awaitingHuman && (
168
  <div className="status-bar">
frontend/src/components/Header.js CHANGED
@@ -1,5 +1,5 @@
1
  import React from 'react';
2
- import { UserPlus, UserCheck } from 'lucide-react';
3
  import AuthBadge from './AuthBadge';
4
  import ParticipantDropdown from './ParticipantDropdown';
5
  import DownloadMenu from './DownloadMenu';
@@ -106,7 +106,7 @@ export default function Header({
106
  {humanParticipant ? (
107
  <>
108
  <UserCheck size={14} style={{ marginRight: 4 }} />
109
- Human: {humanParticipant.name}
110
  </>
111
  ) : (
112
  <>
@@ -115,6 +115,18 @@ export default function Header({
115
  </>
116
  )}
117
  </button>
 
 
 
 
 
 
 
 
 
 
 
 
118
  <DownloadMenu
119
  hasChat={hasChat}
120
  hasApiLog={hasApiLog}
 
1
  import React from 'react';
2
+ import { UserPlus, UserCheck, Table2 } from 'lucide-react';
3
  import AuthBadge from './AuthBadge';
4
  import ParticipantDropdown from './ParticipantDropdown';
5
  import DownloadMenu from './DownloadMenu';
 
106
  {humanParticipant ? (
107
  <>
108
  <UserCheck size={14} style={{ marginRight: 4 }} />
109
+ {humanParticipant.name}
110
  </>
111
  ) : (
112
  <>
 
115
  </>
116
  )}
117
  </button>
118
+ <button
119
+ type="button"
120
+ className="btn-sm btn-outline ccai-table-view-btn"
121
+ onClick={onShowTableView}
122
+ disabled={!hasChat}
123
+ title={hasChat
124
+ ? 'Open the conversation summary table'
125
+ : 'Start a chat to view the summary table'}
126
+ >
127
+ <Table2 size={14} style={{ marginRight: 4 }} />
128
+ Table View
129
+ </button>
130
  <DownloadMenu
131
  hasChat={hasChat}
132
  hasApiLog={hasApiLog}
frontend/src/components/ParticipantSidebar.js CHANGED
@@ -27,15 +27,15 @@ export default function ParticipantSidebar({
27
  <aside className="sidebar ccai-sidebar">
28
  <div className="ccai-sidebar-header">
29
  <h2 className="sidebar-title">Participants</h2>
30
- <div className="ccai-sidebar-help">
31
- {showAutoPlaceholder ? (
32
- <em>Auto-select is on.</em>
33
- ) : participants.length === 0 ? (
34
- <em>Use the Participants dropdown in the header to add some.</em>
35
- ) : (
36
- <em>Drag to reorder is not supported yet — order is by selection.</em>
37
- )}
38
- </div>
39
  </div>
40
  {showAutoPlaceholder && (
41
  <div className="ccai-sidebar-autoselect-empty">
 
27
  <aside className="sidebar ccai-sidebar">
28
  <div className="ccai-sidebar-header">
29
  <h2 className="sidebar-title">Participants</h2>
30
+ {(showAutoPlaceholder || participants.length === 0) && (
31
+ <div className="ccai-sidebar-help">
32
+ {showAutoPlaceholder ? (
33
+ <em>Auto-select is on.</em>
34
+ ) : (
35
+ <em>Use the Participants dropdown in the header to add some.</em>
36
+ )}
37
+ </div>
38
+ )}
39
  </div>
40
  {showAutoPlaceholder && (
41
  <div className="ccai-sidebar-autoselect-empty">
frontend/src/styles/ccai.css CHANGED
@@ -1306,6 +1306,16 @@
1306
  white-space: nowrap;
1307
  }
1308
 
 
 
 
 
 
 
 
 
 
 
1309
  .ccai-human-add-btn-active {
1310
  border-color: #16A34A;
1311
  color: #16A34A;
 
1306
  white-space: nowrap;
1307
  }
1308
 
1309
+ /* Header "Table View" button — links to the same Summary Table modal
1310
+ the Settings menu opens, so users don't have to dig through a menu
1311
+ to view it during/after a chat. */
1312
+ .ccai-table-view-btn {
1313
+ display: inline-flex;
1314
+ align-items: center;
1315
+ gap: 4px;
1316
+ white-space: nowrap;
1317
+ }
1318
+
1319
  .ccai-human-add-btn-active {
1320
  border-color: #16A34A;
1321
  color: #16A34A;
frontend/src/styles/components.css CHANGED
@@ -590,6 +590,21 @@
590
  max-width: 400px;
591
  }
592
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
  /* ── Chat Controls ─────────────────────────────────────────────── */
594
 
595
  .chat-controls {
@@ -1281,6 +1296,16 @@
1281
  font-size: 14px;
1282
  padding: 14px;
1283
  }
 
 
 
 
 
 
 
 
 
 
1284
  .btn-sm {
1285
  padding: 5px 8px;
1286
  font-size: 11px;
 
590
  max-width: 400px;
591
  }
592
 
593
+ /* Download strip rendered below the "End of Chat" marker. Mirrors the
594
+ items in the header DownloadMenu so the user doesn't have to scroll
595
+ back up to the top of a finished conversation to export it. */
596
+ .chat-end-downloads {
597
+ display: flex;
598
+ flex-wrap: wrap;
599
+ justify-content: center;
600
+ gap: 8px;
601
+ padding: 12px 0 4px;
602
+ }
603
+
604
+ .chat-end-download-btn {
605
+ white-space: nowrap;
606
+ }
607
+
608
  /* ── Chat Controls ─────────────────────────────────────────────── */
609
 
610
  .chat-controls {
 
1296
  font-size: 14px;
1297
  padding: 14px;
1298
  }
1299
+ /* On phones the download strip stacks so each label is fully
1300
+ readable; on wider viewports it stays inline-wrap. */
1301
+ .chat-end-downloads {
1302
+ flex-direction: column;
1303
+ align-items: stretch;
1304
+ gap: 6px;
1305
+ }
1306
+ .chat-end-download-btn {
1307
+ justify-content: center;
1308
+ }
1309
  .btn-sm {
1310
  padding: 5px 8px;
1311
  font-size: 11px;