tfrere HF Staff Cursor commited on
Commit
79008fb
·
1 Parent(s): 56a91d6

feat(embed-studio): clickable chart list to switch files

Browse files

The sidebar already lists every chart in the doc but only the
current one was highlighted - the others were inert <li>s. Make
non-current entries clickable so the user can jump between charts
without going back to the article and reopening another embed.

- FilesSidebar: optional onSelectChart prop. Non-current items
render as a <button class="es-files__item-main"> (same look as
the clickable data files), the current item stays a plain <li>
with the "editing" badge.
- EmbedStudio: forwards onSelectChart to the sidebar.
- App: rotates embedStudioSession when switching, so the studio
remounts and useEmbedChat's mount-only loadMessages picks up
the chat history persisted for the newly-selected file.
- CSS: existing .es-files__item--data { padding: 0 } pattern
extended via :has() to any .es-files__item that contains a
button child, so the <li> padding doesn't stack with the
button's own padding.

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

frontend/src/App.tsx CHANGED
@@ -1205,6 +1205,16 @@ export default function App() {
1205
  setEmbedStudioSession(null);
1206
  }}
1207
  onRename={handleEmbedRename}
 
 
 
 
 
 
 
 
 
 
1208
  />
1209
  )}
1210
 
 
1205
  setEmbedStudioSession(null);
1206
  }}
1207
  onRename={handleEmbedRename}
1208
+ onSelectChart={(name) => {
1209
+ if (name === embedStudioSrc) return;
1210
+ setEmbedStudioSrc(name);
1211
+ // Rotate the session key so the studio remounts with a
1212
+ // fresh chat scope (loadMessages picks up the persisted
1213
+ // history of the newly-selected file).
1214
+ setEmbedStudioSession(
1215
+ `es-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
1216
+ );
1217
+ }}
1218
  />
1219
  )}
1220
 
frontend/src/components/EmbedStudio.tsx CHANGED
@@ -27,6 +27,13 @@ interface EmbedStudioProps {
27
  * reopen, etc.) reference the renamed file.
28
  */
29
  onRename?: (oldSrc: string, newSrc: string) => void;
 
 
 
 
 
 
 
30
  }
31
 
32
  const EMBED_TOOL_LABELS: Record<string, [string, string]> = {
@@ -146,6 +153,7 @@ export function EmbedStudio({
146
  userId,
147
  onClose,
148
  onRename,
 
149
  }: EmbedStudioProps) {
150
  const { isDark, primaryColor } = useTheme();
151
  const [html, setHtml] = useState("");
@@ -287,6 +295,7 @@ export function EmbedStudio({
287
  currentSrc={src}
288
  dataFiles={data.files}
289
  selectedDataFile={selectedDataFile}
 
290
  onSelectDataFile={setSelectedDataFile}
291
  onUploadFiles={data.uploadFiles}
292
  onRemoveDataFile={data.removeFile}
 
27
  * reopen, etc.) reference the renamed file.
28
  */
29
  onRename?: (oldSrc: string, newSrc: string) => void;
30
+ /**
31
+ * Invoked when the user picks a different chart from the sidebar.
32
+ * Parent is responsible for updating the studio's `src` and (if it
33
+ * wants a fresh chat scope) rotating the React key on this
34
+ * component so persisted messages reload for the new file.
35
+ */
36
+ onSelectChart?: (name: string) => void;
37
  }
38
 
39
  const EMBED_TOOL_LABELS: Record<string, [string, string]> = {
 
153
  userId,
154
  onClose,
155
  onRename,
156
+ onSelectChart,
157
  }: EmbedStudioProps) {
158
  const { isDark, primaryColor } = useTheme();
159
  const [html, setHtml] = useState("");
 
295
  currentSrc={src}
296
  dataFiles={data.files}
297
  selectedDataFile={selectedDataFile}
298
+ onSelectChart={onSelectChart}
299
  onSelectDataFile={setSelectedDataFile}
300
  onUploadFiles={data.uploadFiles}
301
  onRemoveDataFile={data.removeFile}
frontend/src/components/FilesSidebar.tsx CHANGED
@@ -11,6 +11,12 @@ interface FilesSidebarProps {
11
  currentSrc: string;
12
  dataFiles: EmbedDataFileMeta[];
13
  selectedDataFile: string | null;
 
 
 
 
 
 
14
  onSelectDataFile: (name: string | null) => void;
15
  onUploadFiles: (files: FileList | File[]) => Promise<UploadResult[]>;
16
  onRemoveDataFile: (name: string) => void;
@@ -21,6 +27,7 @@ export function FilesSidebar({
21
  currentSrc,
22
  dataFiles,
23
  selectedDataFile,
 
24
  onSelectDataFile,
25
  onUploadFiles,
26
  onRemoveDataFile,
@@ -57,19 +64,42 @@ export function FilesSidebar({
57
  {charts.length === 0 && (
58
  <li className="es-files__empty">No charts yet</li>
59
  )}
60
- {charts.map((name) => (
61
- <li
62
- key={name}
63
- className={`es-files__item ${name === currentSrc ? "es-files__item--current" : ""}`}
64
- title={name === currentSrc ? "Current chart" : name}
65
- >
66
- <FileText size={12} />
67
- <span className="es-files__name">{name}</span>
68
- {name === currentSrc && (
69
- <span className="es-files__badge">editing</span>
70
- )}
71
- </li>
72
- ))}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  </ul>
74
  </section>
75
 
 
11
  currentSrc: string;
12
  dataFiles: EmbedDataFileMeta[];
13
  selectedDataFile: string | null;
14
+ /**
15
+ * Switch the embed studio to another chart by filename. Optional -
16
+ * when omitted, the chart list renders as read-only labels (the
17
+ * previous behaviour).
18
+ */
19
+ onSelectChart?: (name: string) => void;
20
  onSelectDataFile: (name: string | null) => void;
21
  onUploadFiles: (files: FileList | File[]) => Promise<UploadResult[]>;
22
  onRemoveDataFile: (name: string) => void;
 
27
  currentSrc,
28
  dataFiles,
29
  selectedDataFile,
30
+ onSelectChart,
31
  onSelectDataFile,
32
  onUploadFiles,
33
  onRemoveDataFile,
 
64
  {charts.length === 0 && (
65
  <li className="es-files__empty">No charts yet</li>
66
  )}
67
+ {charts.map((name) => {
68
+ const isCurrent = name === currentSrc;
69
+ const className = `es-files__item ${isCurrent ? "es-files__item--current" : ""}`;
70
+ const inner = (
71
+ <>
72
+ <FileText size={12} />
73
+ <span className="es-files__name">{name}</span>
74
+ {isCurrent && (
75
+ <span className="es-files__badge">editing</span>
76
+ )}
77
+ </>
78
+ );
79
+ if (!onSelectChart || isCurrent) {
80
+ return (
81
+ <li
82
+ key={name}
83
+ className={className}
84
+ title={isCurrent ? "Current chart" : name}
85
+ >
86
+ {inner}
87
+ </li>
88
+ );
89
+ }
90
+ return (
91
+ <li key={name} className={className}>
92
+ <button
93
+ type="button"
94
+ className="es-files__item-main"
95
+ onClick={() => onSelectChart(name)}
96
+ title={`Switch to ${name}`}
97
+ >
98
+ {inner}
99
+ </button>
100
+ </li>
101
+ );
102
+ })}
103
  </ul>
104
  </section>
105
 
frontend/src/styles/components/_embed-studio.css CHANGED
@@ -452,7 +452,8 @@
452
  color: var(--text-color);
453
  }
454
 
455
- .es-files__item--data {
 
456
  padding: 0;
457
  overflow: hidden;
458
  }
 
452
  color: var(--text-color);
453
  }
454
 
455
+ .es-files__item--data,
456
+ .es-files__item:has(> .es-files__item-main) {
457
  padding: 0;
458
  overflow: hidden;
459
  }