Skip to content

Commit 751092e

Browse files
committed
feat: add copy-to-clipboard icon for code blocks in dataset loading tabs
1 parent 5ed2479 commit 751092e

File tree

2 files changed

+69
-52
lines changed

2 files changed

+69
-52
lines changed

src/components/DatasetDetailPage/LoadDatasetTabs.tsx

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Tabs, Tab, Box, Typography } from "@mui/material";
1+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
2+
import { Tabs, Tab, Box, Typography, IconButton, Tooltip } from "@mui/material";
23
import { Colors } from "design/theme";
34
import React from "react";
45
import { useState } from "react";
@@ -57,7 +58,9 @@ const LoadDatasetTabs: React.FC<LoadDatasetTabsProps> = ({
5758
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
5859
setTabIndex(newValue);
5960
};
61+
console.log("datasetDocument", datasetDocument);
6062
const datasetDesc = datasetDocument?.["dataset_description.json"];
63+
console.log("datasetDesc", datasetDesc);
6164
const datasetName = datasetDesc?.Name?.includes(" - ")
6265
? datasetDesc.Name.split(" - ")[1]
6366
: datasetDesc?.Name || datasetDocument?._id || docname;
@@ -81,6 +84,26 @@ const LoadDatasetTabs: React.FC<LoadDatasetTabsProps> = ({
8184
);
8285
};
8386

87+
const CopyableCodeBlock = ({ code }: { code: string }) => {
88+
const handleCopy = () => {
89+
navigator.clipboard.writeText(code);
90+
};
91+
return (
92+
<Box sx={{ position: "relative" }}>
93+
<IconButton
94+
onClick={handleCopy}
95+
size="small"
96+
sx={{ position: "absolute", top: 5, right: 5 }}
97+
>
98+
<Tooltip title="Copy to clipboard">
99+
<ContentCopyIcon fontSize="small" />
100+
</Tooltip>
101+
</IconButton>
102+
<code style={flashcardStyles.codeBlock}>{code}</code>
103+
</Box>
104+
);
105+
};
106+
84107
return (
85108
<>
86109
<Tabs
@@ -118,20 +141,18 @@ const LoadDatasetTabs: React.FC<LoadDatasetTabsProps> = ({
118141
Load by URL with REST-API in Python
119142
</Typography>
120143
<Typography>Install:</Typography>
121-
<code style={flashcardStyles.codeBlock}>
122-
pip install jdata bjdata numpy
123-
</code>
144+
<CopyableCodeBlock code={`pip install jdata bjdata numpy`} />
124145
<Typography>Load from URL:</Typography>
125-
<code style={flashcardStyles.codeBlock}>
126-
{`import jdata as jd
146+
<CopyableCodeBlock
147+
code={`import jdata as jd
127148
data = jd.loadurl('${datasetUrl}')
128149
129150
# List all externally linked files
130151
links = jd.jsonpath(data, '$.._DataLink_')
131152
132153
# Download & cache anatomical nii.gz data for sub-01/sub-02
133154
jd.jdlink(links, {'regex': 'anat/sub-0[12]_.*\\.nii'})`}
134-
</code>
155+
/>
135156
</Box>
136157
</TabPanel>
137158

@@ -142,12 +163,10 @@ jd.jdlink(links, {'regex': 'anat/sub-0[12]_.*\\.nii'})`}
142163
Load by URL with REST-API in MATLAB
143164
</Typography>
144165
<Typography>Install:</Typography>
145-
<code style={flashcardStyles.codeBlock}>
146-
Download and addpath to JSONLab
147-
</code>
166+
<CopyableCodeBlock code={`Download and addpath to JSONLab`} />
148167
<Typography>Load from URL:</Typography>
149-
<code style={flashcardStyles.codeBlock}>
150-
{`data = loadjson('${datasetUrl}');
168+
<CopyableCodeBlock
169+
code={`data = loadjson('${datasetUrl}');
151170
152171
% or without JSONLab (webread cannot decode JData annotations)
153172
data = webread('${datasetUrl}');
@@ -157,7 +176,7 @@ links = jsonpath(data, '$.._DataLink_');
157176
158177
% Download & cache anatomical nii.gz data for sub-01/sub-02
159178
niidata = jdlink(links, 'regex', 'anat/sub-0[12]_.*\\.nii');`}
160-
</code>
179+
/>
161180
</Box>
162181
</TabPanel>
163182

@@ -168,13 +187,9 @@ niidata = jdlink(links, 'regex', 'anat/sub-0[12]_.*\\.nii');`}
168187
Use in MATLAB/Octave
169188
</Typography>
170189
<Typography>Load:</Typography>
171-
<code
172-
style={flashcardStyles.codeBlock}
173-
>{`data = loadjd('${docname}.json');`}</code>
190+
<CopyableCodeBlock code={`data = loadjd('${docname}.json');`} />
174191
<Typography>Read value:</Typography>
175-
<code
176-
style={flashcardStyles.codeBlock}
177-
>{`data.(encodevarname('${onekey}'))`}</code>
192+
<CopyableCodeBlock code={`data.(encodevarname('${onekey}'))`} />
178193
</Box>
179194
</TabPanel>
180195

@@ -185,12 +200,12 @@ niidata = jdlink(links, 'regex', 'anat/sub-0[12]_.*\\.nii');`}
185200
Use in Python
186201
</Typography>
187202
<Typography>Load:</Typography>
188-
<code style={flashcardStyles.codeBlock}>
189-
{`import jdata as jd
203+
<CopyableCodeBlock
204+
code={`import jdata as jd
190205
data = jd.load('${docname}.json')`}
191-
</code>
206+
/>
192207
<Typography>Read value:</Typography>
193-
<code style={flashcardStyles.codeBlock}>{`data["${onekey}"]`}</code>
208+
<CopyableCodeBlock code={`data["${onekey}"]`} />
194209
</Box>
195210
</TabPanel>
196211

@@ -201,21 +216,17 @@ data = jd.load('${docname}.json')`}
201216
Use in C++
202217
</Typography>
203218
<Typography>Install:</Typography>
204-
<code style={flashcardStyles.codeBlock}>
205-
Download JSON for Modern C++ json.hpp
206-
</code>
219+
<CopyableCodeBlock code={`Download JSON for Modern C++ json.hpp`} />
207220
<Typography>Load:</Typography>
208-
<code style={flashcardStyles.codeBlock}>
209-
{`#include "json.hpp"
210-
using json=nlohmann::ordered_json;
211-
212-
std::ifstream datafile("${docname}.json");
213-
json data(datafile);`}
214-
</code>
221+
<CopyableCodeBlock
222+
code={`#include "json.hpp"
223+
using json=nlohmann::ordered_json;
224+
225+
std::ifstream datafile("${docname}.json");
226+
json data(datafile);`}
227+
/>
215228
<Typography>Read value:</Typography>
216-
<code
217-
style={flashcardStyles.codeBlock}
218-
>{`std::cout << data["${onekey}"];`}</code>
229+
<CopyableCodeBlock code={`std::cout << data["${onekey}"];`} />
219230
</Box>
220231
</TabPanel>
221232

@@ -226,24 +237,20 @@ data = jd.load('${docname}.json')`}
226237
Use in JS/Node.js
227238
</Typography>
228239
<Typography>Install:</Typography>
229-
<code style={flashcardStyles.codeBlock}>
230-
npm install jda numjs pako atob
231-
</code>
240+
<CopyableCodeBlock code={`npm install jda numjs pako atob`} />
232241
<Typography>Load:</Typography>
233-
<code style={flashcardStyles.codeBlock}>
234-
{`const fs = require("fs");
235-
const jd = require("jda");
236-
global.atob = require("atob");
242+
<CopyableCodeBlock
243+
code={`const fs = require("fs");
244+
const jd = require("jda");
245+
global.atob = require("atob");
237246
238-
const fn = "${docname}.json";
239-
var jstr = fs.readFileSync(fn).toString().replace(/\\n/g, "");
240-
var data = new jd(JSON.parse(jstr));
241-
data = data.decode();`}
242-
</code>
247+
const fn = "${docname}.json";
248+
var jstr = fs.readFileSync(fn).toString().replace(/\\n/g, "");
249+
var data = new jd(JSON.parse(jstr));
250+
data = data.decode();`}
251+
/>
243252
<Typography>Read value:</Typography>
244-
<code
245-
style={flashcardStyles.codeBlock}
246-
>{`console.log(data.data["${onekey}"]);`}</code>
253+
<CopyableCodeBlock code={`console.log(data.data["${onekey}"]);`} />
247254
</Box>
248255
</TabPanel>
249256
</>

src/pages/DatasetDetailPage.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,19 @@ const DatasetDetailPage: React.FC = () => {
242242
externalLinks.forEach((link) => {
243243
const url = link.url;
244244
const match = url.match(/file=([^&]+)/);
245+
// const filename = match
246+
// ? decodeURIComponent(match[1])
247+
// : `file-${link.index}`;
245248
const filename = match
246-
? decodeURIComponent(match[1])
249+
? (() => {
250+
try {
251+
return decodeURIComponent(match[1]);
252+
} catch {
253+
return match[1]; // fallback if decode fails
254+
}
255+
})()
247256
: `file-${link.index}`;
257+
248258
const outputPath = `$HOME/.neurojson/io/${dbName}/${docId}/${filename}`;
249259

250260
script += `curl -L --create-dirs "${url}" -o "${outputPath}"\n`;

0 commit comments

Comments
 (0)